diff --git a/moto/dynamodb/models/table.py b/moto/dynamodb/models/table.py
--- a/moto/dynamodb/models/table.py
+++ b/moto/dynamodb/models/table.py
@@ -843,6 +843,12 @@ def scan(
             if passes_all_conditions:
                 results.append(item)
 
+        results = copy.deepcopy(results)
+        if index_name:
+            index = self.get_index(index_name)
+            for result in results:
+                index.project(result)
+
         results, last_evaluated_key = self._trim_results(
             results, limit, exclusive_start_key, scanned_index=index_name
         )

Thanks for raising this and providing the test cased @DrGFreeman! Marking it as a bug.
diff --git a/tests/test_dynamodb/test_dynamodb.py b/tests/test_dynamodb/test_dynamodb.py
--- a/tests/test_dynamodb/test_dynamodb.py
+++ b/tests/test_dynamodb/test_dynamodb.py
@@ -4708,6 +4708,17 @@ def test_gsi_projection_type_include():
         }
     )
 
+    # Same when scanning the table
+    items = table.scan(IndexName="GSI-INC")["Items"]
+    items[0].should.equal(
+        {
+            "gsiK1PartitionKey": "gsi-pk",
+            "gsiK1SortKey": "gsi-sk",
+            "partitionKey": "pk-1",
+            "projectedAttribute": "lore ipsum",
+        }
+    )
+
 
 @mock_dynamodb
 def test_lsi_projection_type_keys_only():
@@ -4756,6 +4767,12 @@ def test_lsi_projection_type_keys_only():
         {"partitionKey": "pk-1", "sortKey": "sk-1", "lsiK1SortKey": "lsi-sk"}
     )
 
+    # Same when scanning the table
+    items = table.scan(IndexName="LSI")["Items"]
+    items[0].should.equal(
+        {"lsiK1SortKey": "lsi-sk", "partitionKey": "pk-1", "sortKey": "sk-1"}
+    )
+
 
 @mock_dynamodb
 @pytest.mark.parametrize(

Scan of DynamoDB table with GSI ignores projection type and returns all attributes
## Description

Scanning the global secondary index (GSI) of a dynamodb table with the GSI projection type set to `INCLUDE` or `KEYS_ONLY` returns all the table attributes (i.e. ignores which attributes are projected to the index).

## Steps to reproduce

Run the following tests (using pytest):

```python
import boto3
from moto import mock_dynamodb


@mock_dynamodb
def test_scan_gsi_projection_include_returns_only_included_attributes():
    dynamodb = boto3.resource("dynamodb", region_name="us-east-1")

    TABLE_NAME = "table-with-gsi-project-include"

    dynamodb.create_table(
        AttributeDefinitions=[
            {"AttributeName": "id", "AttributeType": "S"},
        ],
        TableName=TABLE_NAME,
        KeySchema=[
            {"AttributeName": "id", "KeyType": "HASH"},
        ],
        GlobalSecondaryIndexes=[
            {
                "IndexName": "proj-include",
                "KeySchema": [
                    {"AttributeName": "id", "KeyType": "HASH"},
                ],
                "Projection": {
                    "ProjectionType": "INCLUDE",
                    "NonKeyAttributes": ["attr2"],
                },
                "ProvisionedThroughput": {
                    "ReadCapacityUnits": 10,
                    "WriteCapacityUnits": 10,
                },
            },
        ],
        BillingMode="PROVISIONED",
        ProvisionedThroughput={"ReadCapacityUnits": 10, "WriteCapacityUnits": 10},
    )

    table = dynamodb.Table(TABLE_NAME)

    for id in "A", "B", "C":
        table.put_item(Item=dict(id=id, attr1=f"attr1_{id}", attr2=f"attr2_{id}"))

    items = table.scan(IndexName="proj-include")["Items"]

    # Only id (key) and attr2 (projected) attributes are expected.
    # Currently, all attribtues are returned.
    assert sorted(items[0].keys()) == sorted(["id", "attr2"])


@mock_dynamodb
def test_scan_gsi_projection_keys_only_returns_only_key_attributes():
    dynamodb = boto3.resource("dynamodb", region_name="us-east-1")

    TABLE_NAME = "table-with-gsi-project-keys-only"

    dynamodb.create_table(
        AttributeDefinitions=[
            {"AttributeName": "id", "AttributeType": "S"},
            {"AttributeName": "seq", "AttributeType": "N"},
        ],
        TableName=TABLE_NAME,
        KeySchema=[
            {"AttributeName": "id", "KeyType": "HASH"},
            {"AttributeName": "seq", "KeyType": "RANGE"},
        ],
        GlobalSecondaryIndexes=[
            {
                "IndexName": "proj-keys-only",
                "KeySchema": [
                    {"AttributeName": "id", "KeyType": "HASH"},
                    {"AttributeName": "seq", "KeyType": "RANGE"},
                ],
                "Projection": {
                    "ProjectionType": "KEYS_ONLY",
                },
                "ProvisionedThroughput": {
                    "ReadCapacityUnits": 10,
                    "WriteCapacityUnits": 10,
                },
            },
        ],
        BillingMode="PROVISIONED",
        ProvisionedThroughput={"ReadCapacityUnits": 10, "WriteCapacityUnits": 10},
    )

    table = dynamodb.Table(TABLE_NAME)

    for id in "A", "B", "C":
        table.put_item(Item=dict(id=id, seq=0, attr1=f"attr1_{id}0"))
        table.put_item(Item=dict(id=id, seq=1, attr1=f"attr1_{id}1"))

    items = table.scan(IndexName="proj-keys-only")["Items"]

    # Only id and seq (key) attributes are expected.
    # Currently, all attribtues are returned.
    assert sorted(items[0].keys()) == sorted(["id", "seq"])
```

## Expected behavior

The two tests are expected to pass, i.e. only the attributes projected to the GSI are returned in the scanned items.

## Actual behavior

In both tests, all the table attributes are returned in the scanned items.

Output of first test:
```
>       assert sorted(items[0].keys()) == sorted(["id", "attr2"])
E       AssertionError: assert ['attr1', 'attr2', 'id'] == ['attr2', 'id']
E         At index 0 diff: 'attr1' != 'attr2'
E         Left contains one more item: 'id'
E         Full diff:
E         - ['attr2', 'id']
E         + ['attr1', 'attr2', 'id']
E         ?  +++++++++
```

Output of second test:
```
>       assert sorted(items[0].keys()) == sorted(["id", "seq"])
E       AssertionError: assert ['attr1', 'id', 'seq'] == ['id', 'seq']
E         At index 0 diff: 'attr1' != 'id'
E         Left contains one more item: 'seq'
E         Full diff:
E         - ['id', 'seq']
E         + ['attr1', 'id', 'seq']
```

## Notes

moto installed via pip, Python 3.10 on Ubuntu 20.04:

```
boto3              1.26.74
botocore           1.29.74
moto               4.1.3
```

tests/test_dynamodb/test_dynamodb.py::test_gsi_projection_type_include tests/test_dynamodb/test_dynamodb.py::test_lsi_projection_type_keys_only
tests/test_dynamodb/test_dynamodb.py::test_remove_list_index__remove_existing_nested_index tests/test_dynamodb/test_dynamodb.py::test_basic_projection_expressions_using_query_with_attr_expression_names tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_put_conditional_expressions_return_values_on_condition_check_failure_all_old tests/test_dynamodb/test_dynamodb.py::test_describe_backup_for_non_existent_backup_raises_error tests/test_dynamodb/test_dynamodb.py::test_update_item_with_list tests/test_dynamodb/test_dynamodb.py::test_describe_continuous_backups_errors tests/test_dynamodb/test_dynamodb.py::test_describe_missing_table_boto3 tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_delete tests/test_dynamodb/test_dynamodb.py::test_remove_list_index__remove_existing_index tests/test_dynamodb/test_dynamodb.py::test_dynamodb_update_item_fails_on_string_sets tests/test_dynamodb/test_dynamodb.py::test_projection_expression_execution_order tests/test_dynamodb/test_dynamodb.py::test_update_item_with_attribute_in_right_hand_side_and_operation tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_conditioncheck_passes tests/test_dynamodb/test_dynamodb.py::test_basic_projection_expression_using_get_item tests/test_dynamodb/test_dynamodb.py::test_list_table_tags_empty tests/test_dynamodb/test_dynamodb.py::test_create_backup_for_non_existent_table_raises_error tests/test_dynamodb/test_dynamodb.py::test_update_expression_with_plus_in_attribute_name tests/test_dynamodb/test_dynamodb.py::test_scan_filter2 tests/test_dynamodb/test_dynamodb.py::test_describe_backup tests/test_dynamodb/test_dynamodb.py::test_batch_write_item tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_delete_with_successful_condition_expression tests/test_dynamodb/test_dynamodb.py::test_query_invalid_table tests/test_dynamodb/test_dynamodb.py::test_delete_backup tests/test_dynamodb/test_dynamodb.py::test_nested_projection_expression_using_query tests/test_dynamodb/test_dynamodb.py::test_transact_get_items_should_return_empty_map_for_non_existent_item tests/test_dynamodb/test_dynamodb.py::test_update_supports_list_append_maps tests/test_dynamodb/test_dynamodb.py::test_update_supports_nested_update_if_nested_value_not_exists tests/test_dynamodb/test_dynamodb.py::test_index_with_unknown_attributes_should_fail tests/test_dynamodb/test_dynamodb.py::test_update_supports_nested_list_append tests/test_dynamodb/test_dynamodb.py::test_update_item_atomic_counter_from_zero tests/test_dynamodb/test_dynamodb.py::test_update_item_if_original_value_is_none tests/test_dynamodb/test_dynamodb.py::test_filter_expression_execution_order tests/test_dynamodb/test_dynamodb.py::test_delete_item tests/test_dynamodb/test_dynamodb.py::test_list_tables_paginated tests/test_dynamodb/test_dynamodb.py::test_query_gsi_with_range_key tests/test_dynamodb/test_dynamodb.py::test_valid_transact_get_items tests/test_dynamodb/test_dynamodb.py::test_describe_continuous_backups tests/test_dynamodb/test_dynamodb.py::test_remove_list_index__remove_existing_double_nested_index tests/test_dynamodb/test_dynamodb.py::test_non_existing_attribute_should_raise_exception tests/test_dynamodb/test_dynamodb.py::test_scan_by_non_exists_index tests/test_dynamodb/test_dynamodb.py::test_update_supports_list_append_with_nested_if_not_exists_operation tests/test_dynamodb/test_dynamodb.py::test_put_empty_item tests/test_dynamodb/test_dynamodb.py::test_gsi_lastevaluatedkey tests/test_dynamodb/test_dynamodb.py::test_query_catches_when_no_filters tests/test_dynamodb/test_dynamodb.py::test_restore_table_to_point_in_time_raises_error_when_dest_exist tests/test_dynamodb/test_dynamodb.py::test_update_supports_complex_expression_attribute_values tests/test_dynamodb/test_dynamodb.py::test_update_expression_with_numeric_literal_instead_of_value tests/test_dynamodb/test_dynamodb.py::test_list_tables_boto3[multiple-tables] tests/test_dynamodb/test_dynamodb.py::test_update_list_index__set_nested_index_out_of_range tests/test_dynamodb/test_dynamodb.py::test_update_nested_item_if_original_value_is_none tests/test_dynamodb/test_dynamodb.py::test_update_supports_multiple_levels_nested_list_append tests/test_dynamodb/test_dynamodb.py::test_list_tables_boto3[one-table] tests/test_dynamodb/test_dynamodb.py::test_put_item_nonexisting_range_key tests/test_dynamodb/test_dynamodb.py::test_list_backups tests/test_dynamodb/test_dynamodb.py::test_query_filter_overlapping_expression_prefixes tests/test_dynamodb/test_dynamodb.py::test_source_and_restored_table_items_are_not_linked tests/test_dynamodb/test_dynamodb.py::test_bad_scan_filter tests/test_dynamodb/test_dynamodb.py::test_update_expression_with_minus_in_attribute_name tests/test_dynamodb/test_dynamodb.py::test_restore_table_from_backup_raises_error_when_table_already_exists tests/test_dynamodb/test_dynamodb.py::test_put_item_nonexisting_hash_key tests/test_dynamodb/test_dynamodb.py::test_restore_table_to_point_in_time_raises_error_when_source_not_exist tests/test_dynamodb/test_dynamodb.py::test_basic_projection_expression_using_get_item_with_attr_expression_names tests/test_dynamodb/test_dynamodb.py::test_invalid_transact_get_items tests/test_dynamodb/test_dynamodb.py::test_update_continuous_backups tests/test_dynamodb/test_dynamodb.py::test_update_item_atomic_counter tests/test_dynamodb/test_dynamodb.py::test_nested_projection_expression_using_query_with_attr_expression_names tests/test_dynamodb/test_dynamodb.py::test_item_add_empty_string_range_key_exception tests/test_dynamodb/test_dynamodb.py::test_get_item_for_non_existent_table_raises_error tests/test_dynamodb/test_dynamodb.py::test_scan_filter tests/test_dynamodb/test_dynamodb.py::test_error_when_providing_expression_and_nonexpression_params tests/test_dynamodb/test_dynamodb.py::test_update_return_attributes tests/test_dynamodb/test_dynamodb.py::test_item_size_is_under_400KB tests/test_dynamodb/test_dynamodb.py::test_multiple_updates tests/test_dynamodb/test_dynamodb.py::test_scan_filter4 tests/test_dynamodb/test_dynamodb.py::test_nested_projection_expression_using_get_item_with_attr_expression tests/test_dynamodb/test_dynamodb.py::test_update_expression_with_space_in_attribute_name tests/test_dynamodb/test_dynamodb.py::test_create_multiple_backups_with_same_name tests/test_dynamodb/test_dynamodb.py::test_gsi_projection_type_keys_only tests/test_dynamodb/test_dynamodb.py::test_update_list_index__set_existing_nested_index tests/test_dynamodb/test_dynamodb.py::test_set_attribute_is_dropped_if_empty_after_update_expression[use tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_put_conditional_expressions tests/test_dynamodb/test_dynamodb.py::test_restore_table_from_non_existent_backup_raises_error tests/test_dynamodb/test_dynamodb.py::test_query_by_non_exists_index tests/test_dynamodb/test_dynamodb.py::test_gsi_key_cannot_be_empty tests/test_dynamodb/test_dynamodb.py::test_remove_list_index__remove_index_out_of_range tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_fails_with_transaction_canceled_exception tests/test_dynamodb/test_dynamodb.py::test_update_if_not_exists tests/test_dynamodb/test_dynamodb.py::test_remove_top_level_attribute tests/test_dynamodb/test_dynamodb.py::test_restore_table_from_backup tests/test_dynamodb/test_dynamodb.py::test_nested_projection_expression_using_scan_with_attr_expression_names tests/test_dynamodb/test_dynamodb.py::test_update_non_existing_item_raises_error_and_does_not_contain_item_afterwards tests/test_dynamodb/test_dynamodb.py::test_allow_update_to_item_with_different_type tests/test_dynamodb/test_dynamodb.py::test_describe_limits tests/test_dynamodb/test_dynamodb.py::test_sorted_query_with_numerical_sort_key tests/test_dynamodb/test_dynamodb.py::test_dynamodb_max_1mb_limit tests/test_dynamodb/test_dynamodb.py::test_update_continuous_backups_errors tests/test_dynamodb/test_dynamodb.py::test_list_backups_for_non_existent_table tests/test_dynamodb/test_dynamodb.py::test_duplicate_create tests/test_dynamodb/test_dynamodb.py::test_summing_up_2_strings_raises_exception tests/test_dynamodb/test_dynamodb.py::test_update_item_atomic_counter_return_values tests/test_dynamodb/test_dynamodb.py::test_put_item_with_special_chars tests/test_dynamodb/test_dynamodb.py::test_query_missing_expr_names tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_update_with_failed_condition_expression tests/test_dynamodb/test_dynamodb.py::test_describe_endpoints[eu-central-1] tests/test_dynamodb/test_dynamodb.py::test_create_backup tests/test_dynamodb/test_dynamodb.py::test_query_filter tests/test_dynamodb/test_dynamodb.py::test_list_tables_boto3[no-table] tests/test_dynamodb/test_dynamodb.py::test_attribute_item_delete tests/test_dynamodb/test_dynamodb.py::test_invalid_projection_expressions tests/test_dynamodb/test_dynamodb.py::test_update_list_index__set_existing_index tests/test_dynamodb/test_dynamodb.py::test_nested_projection_expression_using_get_item tests/test_dynamodb/test_dynamodb.py::test_nested_projection_expression_using_scan tests/test_dynamodb/test_dynamodb.py::test_update_item_on_map tests/test_dynamodb/test_dynamodb.py::test_restore_table_to_point_in_time tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_put tests/test_dynamodb/test_dynamodb.py::test_basic_projection_expressions_using_query tests/test_dynamodb/test_dynamodb.py::test_list_not_found_table_tags tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_update tests/test_dynamodb/test_dynamodb.py::test_update_return_updated_new_attributes_when_same tests/test_dynamodb/test_dynamodb.py::test_gsi_verify_negative_number_order tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_conditioncheck_fails tests/test_dynamodb/test_dynamodb.py::test_set_ttl tests/test_dynamodb/test_dynamodb.py::test_scan_filter_should_not_return_non_existing_attributes tests/test_dynamodb/test_dynamodb.py::test_update_supports_list_append tests/test_dynamodb/test_dynamodb.py::test_put_return_attributes tests/test_dynamodb/test_dynamodb.py::test_remove_list_index__remove_multiple_indexes tests/test_dynamodb/test_dynamodb.py::test_update_list_index__set_double_nested_index tests/test_dynamodb/test_dynamodb.py::test_gsi_key_can_be_updated tests/test_dynamodb/test_dynamodb.py::test_put_item_with_streams tests/test_dynamodb/test_dynamodb.py::test_list_tables_exclusive_start_table_name_empty tests/test_dynamodb/test_dynamodb.py::test_update_item_with_no_action_passed_with_list tests/test_dynamodb/test_dynamodb.py::test_update_item_with_empty_string_attr_no_exception tests/test_dynamodb/test_dynamodb.py::test_transact_write_items_delete_with_failed_condition_expression tests/test_dynamodb/test_dynamodb.py::test_basic_projection_expressions_using_scan tests/test_dynamodb/test_dynamodb.py::test_list_table_tags tests/test_dynamodb/test_dynamodb.py::test_update_supports_nested_list_append_onto_another_list tests/test_dynamodb/test_dynamodb.py::test_update_list_index__set_index_out_of_range tests/test_dynamodb/test_dynamodb.py::test_filter_expression tests/test_dynamodb/test_dynamodb.py::test_basic_projection_expressions_using_scan_with_attr_expression_names tests/test_dynamodb/test_dynamodb.py::test_item_add_empty_string_hash_key_exception tests/test_dynamodb/test_dynamodb.py::test_item_add_empty_string_attr_no_exception tests/test_dynamodb/test_dynamodb.py::test_update_item_add_to_non_existent_set tests/test_dynamodb/test_dynamodb.py::test_query_global_secondary_index_when_created_via_update_table_resource tests/test_dynamodb/test_dynamodb.py::test_update_item_add_to_non_existent_number_set tests/test_dynamodb/test_dynamodb.py::test_update_expression_with_multiple_set_clauses_must_be_comma_separated tests/test_dynamodb/test_dynamodb.py::test_scan_filter3 tests/test_dynamodb/test_dynamodb.py::test_update_list_index__set_index_of_a_string tests/test_dynamodb/test_dynamodb.py::test_update_supports_list_append_with_nested_if_not_exists_operation_and_property_already_exists tests/test_dynamodb/test_dynamodb.py::test_describe_endpoints[ap-south-1] tests/test_dynamodb/test_dynamodb.py::test_delete_table tests/test_dynamodb/test_dynamodb.py::test_update_item_add_to_list_using_legacy_attribute_updates tests/test_dynamodb/test_dynamodb.py::test_remove_top_level_attribute_non_existent tests/test_dynamodb/test_dynamodb.py::test_delete_non_existent_backup_raises_error tests/test_dynamodb/test_dynamodb.py::test_update_item_with_attribute_in_right_hand_side tests/test_dynamodb/test_dynamodb.py::test_list_table_tags_paginated