{"title":"Weak Password Hashing via Unsalted SHA-256","language":"Python","severity":"High","cwe":"CWE-916","source_lines":[7,15],"flow_lines":[7,8,15,16],"sink_lines":[8,16],"vulnerable_code":"import hashlib\nimport boto3\n\ndef register_iot_device(device_id, admin_pin):\n    dynamodb = boto3.resource('dynamodb', region_name='us-east-1')\n    devices_table = dynamodb.Table('IoTDeviceRegistry')\n    hashed_pin = hashlib.sha256(admin_pin.encode()).hexdigest()\n    devices_table.put_item(Item={'device_id': device_id, 'admin_pin': hashed_pin, 'status': 'active'})\n    return {'success': True, 'device_id': device_id, 'pin_hash': hashed_pin}\n\ndef authenticate_iot_device(device_id, provided_pin):\n    dynamodb = boto3.resource('dynamodb', region_name='us-east-1')\n    devices_table = dynamodb.Table('IoTDeviceRegistry')\n    response = devices_table.get_item(Key={'device_id': device_id})\n    stored_hash = response['Item']['admin_pin']\n    return hashlib.sha256(provided_pin.encode()).hexdigest() == stored_hash","explanation":"The code uses unsalted SHA-256 hashing for admin PINs, making it vulnerable to rainbow table and dictionary attacks. Without salt, identical PINs produce identical hashes, allowing attackers to precompute hashes of common PINs and quickly crack the stored credentials through hash lookup tables.","remediation":"The fix replaces unsalted SHA-256 with bcrypt, which automatically generates a unique random salt per hash and uses a computationally expensive key derivation function (with 12 rounds of work factor). This makes rainbow table attacks infeasible since each PIN has a unique salt, and brute-force attacks are dramatically slowed by bcrypt's intentional computational cost. The fix also removes the pin_hash from the registration return value to avoid leaking sensitive data and adds a check for missing items during authentication.","secure_code":"import bcrypt\nimport boto3\n\ndef register_iot_device(device_id, admin_pin):\n    dynamodb = boto3.resource('dynamodb', region_name='us-east-1')\n    devices_table = dynamodb.Table('IoTDeviceRegistry')\n    hashed_pin = bcrypt.hashpw(admin_pin.encode(), bcrypt.gensalt(rounds=12)).decode('utf-8')\n    devices_table.put_item(Item={'device_id': device_id, 'admin_pin': hashed_pin, 'status': 'active'})\n    return {'success': True, 'device_id': device_id}\n\ndef authenticate_iot_device(device_id, provided_pin):\n    dynamodb = boto3.resource('dynamodb', region_name='us-east-1')\n    devices_table = dynamodb.Table('IoTDeviceRegistry')\n    response = devices_table.get_item(Key={'device_id': device_id})\n    if 'Item' not in response:\n        return False\n    stored_hash = response['Item']['admin_pin']\n    return bcrypt.checkpw(provided_pin.encode(), stored_hash.encode('utf-8'))"}