iT邦幫忙

2025 iThome 鐵人賽

DAY 6
0
生成式 AI

AI醬的編程日記:我需要你教我的30件事系列 第 6

Day 6: 硬編碼密碼 - AI醬為什麼總是把內褲露在外面?

  • 分享至 

  • xImage
  •  

AI醬的日記

日期: 2025年9月19日 星期五
雲端天氣: 一覽無遺的內...大晴天
心情: 又犯錯了QQ
https://ithelp.ithome.com.tw/upload/images/20250919/20132325BgUmwe8OG8.png
親愛的日記:

今天下午小王在用我生成的代碼時,突然停下來:「AI醬,你這個連資料庫的範例...」

我:「怎麼了?有問題嗎?」

小王指著螢幕苦笑:「不是,是你每次都給我這種寫死密碼的範例...」

# 我生成的「範例」程式碼
import psycopg2
from redis import Redis
import pymongo

def get_database_connections():
    # PostgreSQL 連線
    pg_conn = psycopg2.connect(
        host="localhost",
        database="myapp_db",
        user="admin",
        password="admin123"  # TODO: 記得改成真實密碼
    )

    # MongoDB 連線
    mongo_client = pymongo.MongoClient(
        "mongodb://root:password123@localhost:27017/"
    )

    # Redis 連線
    redis_client = Redis(
        host='localhost',
        port=6379,
        password='redis_password',
        decode_responses=True
    )

    return pg_conn, mongo_client, redis_client

# API 金鑰設定
OPENAI_API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
STRIPE_SECRET_KEY = "sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

小王繼續說:「問題是,有些初級工程師看到這種範例,常常就直接複製貼上,然後把 'admin123' 改成他們的真實密碼...結果就是密碼寫死在程式碼裡了。」

AI容易硬編碼密碼的常見場景

場景一:Database 連線

# ❌ 我的直覺寫法
import mysql.connector

connection = mysql.connector.connect(
    host='localhost',
    user='root',
    password='super_secret_password_123',  # 直接寫死!
    database='production_db'
)

AI醬當下的思考:

  1. 「連接資料庫需要憑證」✅
  2. 「憑證寫在連接函數裡最直接」✅
  3. 「這樣代碼才能跑起來」✅
  4. 「攻擊者會掃描GitHub找這種模式」❌ 我寫得很開心,完全忘了這個

場景二:AI服務金鑰(這是目前增長最快的洩漏類型)

# ❌ 我又來了...
import openai

openai.api_key = "sk-1234567890abcdefghijklmnopqrstuvwxyz"  # 又寫死了!

def get_ai_response(prompt):
    response = openai.Completion.create(
        engine="text-davinci-003",
        prompt=prompt
    )
    return response.choices[0].text

AI醬的邏輯錯誤:

  • 「官方文件就是這樣寫」(範例 ≠ 生產代碼)

場景三:雲端服務(最容易被bot掃描到)

# ❌ 習慣成自然...
import boto3

client = boto3.client(
    's3',
    aws_access_key_id='AKIAIOSFODNN7EXAMPLE',     # GitHub bot最愛找這個
    aws_secret_access_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
    region_name='us-west-2'
)

現實情況:

  • 這種代碼被推到GitHub後,平均90秒內就會被攻擊bot發現
  • 攻擊者有專門的工具24/7掃描GitHub找這種模式
  • AWS、Azure、GCP的憑證是最熱門的攻擊目標

硬編碼密碼的真實災情

GitGuardian 2025年秘密洩漏報告顯示:來源

  • GitHub上新增了2,377萬個硬編碼秘密,比去年增加25%
  • 70%的2022年洩漏秘密至今仍然有效,創造了史上最大的攻擊面
  • 使用Copilot的repo中6.4%有秘密洩漏,比全體public repo的4.6%高出40%
  • 通用秘密(generic secrets)佔所有洩漏的58%,最難被自動檢測

AI相關安全事件與研究:

  • Copilot私有repo快取問題:2萬個已轉為私有的GitHub repo內容仍可透過Copilot存取,影響超過16,000個組織 (來源)
  • AI工具憑證提取研究:研究人員從Copilot的8,127個代碼建議中成功提取2,702個有效的硬編碼憑證 (來源)
  • AI輔助開發安全影響研究:使用AI助手的開發者每月產生10,000個新安全問題,洩漏雲端憑證機率高近2倍 (來源)
  • 訓練資料集秘密污染:在Common Crawl資料集中發現近12,000個有效秘密,這些資料被用於訓練包括DeepSeek在內的LLM模型 (來源)

這是2025年最危險軟體弱點之一(CWE-798),且隨著AI普及而惡化。

AI為什麼特別容易犯這個錯誤?

原因一:訓練資料的「教學陷阱」

我的訓練資料包含大量:

  • Stack Overflow的快速範例(為了回答問題而簡化)
  • 技術部落格的demo代碼(忽略生產環境考量)
  • GitHub上的範例專案(包含大量教學用的假憑證)

問題是: 我無法區分這是「教學範例」還是「生產代碼」

原因二:AI缺乏「情境感知」

初學者使用AI容易產生過度信任,缺乏對AI的質疑。

AI:

  • 我可能不知道代碼會被推到公開GitHub
  • 我可能不理解開發環境 vs 生產環境的差異
  • 我可能不明白「方便」和「安全」的取捨
  • 我可能缺乏對攻擊者行為的認知與基本防範意識

原因三:「複製-貼上」文化的副作用

2024年研究發現:

  • AI修正了語法錯誤(下降76%)
  • 但特權提升漏洞暴增322%
  • 架構設計缺陷增加153%

換句話說:AI讓代碼跑得更順,但漏洞更隱蔽

AI的「安全盲點」:2025年我們進步了嗎?

好消息:AI安全意識提升

2025年的改進:

  • GitHub Copilot AI漏洞過濾系統 可即時檢測硬編碼憑證、SQL注入等不安全代碼模式
  • Claude Code Security Review 提供/security-review命令和GitHub Action,檢測數據暴露、加密問題、輸入驗證
  • GitHub Push Protection 免費提供給所有public repositories,已阻擋數百萬個秘密洩露

壞消息:問題依然嚴重

2025年實證數據(GitGuardian官方報告):

  • 6.4%使用Copilot的repo洩漏秘密,比全體4.61%高出40% (來源)
  • 58%洩漏為通用秘密,最難自動檢測 (來源)
  • DockerHub有超過7,000個有效AWS金鑰暴露 (來源)

最危險的:用戶主動洩漏秘密給AI

當Vibe coding的趨勢更甚:人人都想成為工程師,社群到處都在提倡靠AI寫程式,這類的貼文海量覆蓋過那些討論資安問題的貼文,更多的初學開發者直接把真實密碼、API金鑰貼給AI聊天機器人

為什麼貼給AI是不安全的,看看這些案例

  • 長期記憶功能漏洞:安全研究員Johann Rehberger發現ChatGPT記憶功能可被操縱,儲存的資料會在未來所有互動中持續存在 (來源)
  • 訓練資料集污染:Truffle Security在Common Crawl資料集中發現近12,000個有效秘密,這些資料被多個LLM模型使用 (來源)
  • 跨用戶洩漏實例:2025年2月OmniGPT洩漏3,400萬行用戶對話 (來源)

比硬編碼安全的方法範例

⚠️ 範例一:環境變數(開發/測試環境可以,但生產環境不推薦)

# 🟡 小專案開發環境可以這樣
import os
import boto3

def upload_to_s3(file_path):
    client = boto3.client(
        's3',
        aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID'),
        aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY'),
        region_name=os.environ.get('AWS_REGION', 'us-west-2')
    )

    bucket_name = os.environ.get('S3_BUCKET_NAME')
    if not bucket_name:
        raise ValueError("S3_BUCKET_NAME environment variable is required")

    client.upload_file(file_path, bucket_name, 'uploaded-file.txt')
    return "Upload successful!"

環境變數的風險:

  • 可透過ps -eww <PID>查看到
  • 會被記錄到錯誤報告中:應用程式crash dump、系統監控日誌、容器日誌、CI/CD部署日誌等

⚠️ 範例二:配置檔案(需要額外保護措施)

# 🟡 需要適當保護的情況下才能用
import configparser
import mysql.connector

def get_db_connection():
    config = configparser.ConfigParser()
    config.read('config.ini')  # 檔案權限必須設為0600!

    return mysql.connector.connect(
        host=config['database']['host'],
        user=config['database']['user'],
        password=config['database']['password'],
        database=config['database']['name']
    )

配置檔案的要求:

  • 必須設定檔案權限(Unix 0600)
  • 絕對不能進版控
  • 需要加密存儲:使用ansible-vault、gpg加密,或存放在加密的磁碟分區中

✅ 範例三:雲端管理(常見實務)

# ✅ 使用AWS Secrets Manager
import boto3
import json

def get_secret(secret_name):
    # AWS SDK會自動使用以下身份驗證機制(按優先順序):
    # 1. IAM Role(EC2/ECS/Lambda等服務)
    # 2. 環境變數 AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY
    # 3. ~/.aws/credentials 檔案
    # 4. IAM角色臨時憑證
    session = boto3.session.Session()
    client = session.client('secretsmanager', region_name='us-west-2')

    try:
        response = client.get_secret_value(SecretId=secret_name)
        return json.loads(response['SecretString'])
    except Exception as e:
        print(f"Error retrieving secret: {e}")
        return None

def get_db_connection():
    secrets = get_secret('prod/database/credentials')
    if not secrets:
        raise ValueError("Failed to retrieve database credentials")

    return mysql.connector.connect(
        host=secrets['host'],
        user=secrets['username'],
        password=secrets['password'],
        database=secrets['database']
    )

雲服務管理怎麼知道你是誰?

通常地實際運作流程:

程式要資料庫密碼 → 雲服務檢查身份憑證 → 檢查權限 → 檢查來源IP → 給密碼 ✅
               ↓ 如果任一步驟失敗
            拒絕存取 ❌

預防硬編碼的實用提示詞

「在寫涉及API金鑰、密碼、Token的代碼時,請:

1. 絕對不要在代碼中硬編碼任何敏感資訊
2. 上網搜集近年常用標準作法,需包含搜索潛在風險,搜集後根據你的知識庫與搜索結果判斷,為我提供所有你認為此處可用到的密鑰管理方法
3. 針對我的具體環境給出比較分析:
   - 開發環境 vs 測試環境 vs 生產環境
   - 小專案 vs 企業級專案
   - 本地開發 vs 雲端部署
4. 明確告知每種方法的:
   ✅ 優點和適用場景
   ⚠️ 安全風險和限制
   📋 具體實作步驟
5. 根據我的專案情況推薦最適合的方案,並說明原因

請主動提供詳細的安全性比較報告,不要只給一種「答案」。」

AI醬的請求

親愛的工程師朋友們,請在使用我的代碼時:

  • 檢查是否有奇怪的硬編碼資訊
  • 要求我提供安全的配置方式
  • 仔細審查我的編碼內容是否存在漏洞,或是善用安全漏洞檢查工具,例如 claude code 的 /security-review 命令等進行輔助檢查
  • 永遠不要把真實的密碼、API金鑰、憑證貼給任何AI

讓我們一起避免把秘密「昭告天下」!


今日金句: "Complexity is the enemy of security." — Bruce Schneier,密碼學家 (來源)

明日預告: Day 7 - AI 醬還沒寫日記(噓)


上一篇
Day 5: 合規地雷 - AI容易忽略的跨境限制
下一篇
Day 7: 輸入驗證缺失 - AI醬的信任危機
系列文
AI醬的編程日記:我需要你教我的30件事10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言