2024年1月4日 星期四

Python 學習筆記 : 如何隱藏 API 金鑰或權杖

在使用 API 服務 (例如 Line Notify, OpenAI) 時都要申請金鑰 (Key) 或權杖 (Token), 於程式中呼叫 API 時必須同時送出金鑰或權杖才能獲得回應. 但把它們以明碼方式放在程式中並不安全, 常常在分享程式時不慎洩漏出去, 最好是將它們隱藏在系統變數裡, 與程式分離開來較安全, 有兩個好用的 Python 第三方模組可以輕鬆辦到 : python-decouple 與 python-dotenv:


1. 使用 python-decouple 套件儲存環境變數 : 

python-decouple 為第三方套件, 須先用 pip install 指令安裝 : 

pip install python-decouple   

D:\python\test>pip install python-decouple    
Collecting python-decouple
  Downloading python_decouple-3.8-py3-none-any.whl (9.9 kB)
Installing collected packages: python-decouple
Successfully installed python-decouple-3.8

教學文件參考 :


首先在專案工作目錄下用記事本新增一個檔名為 .env 的純文字檔, 以如下格式將金鑰或權杖值設為環境變數 :

環境變數名稱=金鑰/權杖

可以同時定義多個環境變數 (一列一個, 環境變數名稱習慣上使用全大寫), 但要注意右邊的值不可以加引號. 然後將此 .env 檔以 utf-8 編碼方式存檔. 這種以點號開頭的都是隱藏檔, 必須在檔案總管的 "檢視/顯示" 中勾選 "隱藏的檔案" 才看得到. 

例如 :

LINE_NOTIFY_TOKEN=ud7PaDL45fz849A0e1f5oaMCbRIkxMXapQCt7PfNkzz  

使用時先從 decouple (注意不是 python-decouple) 匯入 config() 函式 : 

from decouple import config

然後呼叫 config() 並傳入環境變數名稱即可取得所儲存之金鑰或權杖 :

line_token=config('LINE_NOTIFY_TOKEN')     # 取得 Line Notify Token

例如 : 

>>> from decouple import config    
>>> config('LINE_NOTIFY_TOKEN')    
'ud7PaDL45fz849A0e1f5oaMCbRIkxMXapQCt7PfNkzz'

也可以用內建模組 os 的 environ 字典來檢視或取得環境變數 : 
 
>>> import os   
>>> os.environ['LINE_NOTIFY_TOKEN']   
'ud7PaDL45fz849A0e1f5oaMCbRIkxMXapQCt7PfNkzz'

但這種方法在環境變數不存在時會拋出 KeyError 例外, 須用 try except 捕捉, 例如 : 

>>> os.environ['OPENAI_API_KEY']    
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\tony1\AppData\Local\Programs\Thonny\lib\os.py", line 680, in __getitem__
    raise KeyError(key) from None
KeyError: 'OPENAI_API_KEY'

最好是呼叫 os.environ.get() 或 os.getenv() 函式來取得, 若變數不存在時會傳回 None : 

>>> os.environ.get('LINE_NOTIFY_TOKEN')    
'ud7PaDL45fz849A0e1f5oaMCbRIkxMXapQCt7PfNkzz'
>>> os.getenv('LINE_NOTIFY_TOKEN')    
'ud7PaDL45fz849A0e1f5oaMCbRIkxMXapQCt7PfNkzz'
>>> print(os.environ.get('OPENAI_API_KEY'))    
None   


2. 使用 python-dotenv 套件儲存環境變數 : 

python-dotenv 也是第三方套件, 須先用 pip install 指令安裝 : 

pip install python-dotenv   

D:\python\test>pip install python-dotenv      
Collecting python-dotenv
  Downloading python_dotenv-1.0.0-py3-none-any.whl (19 kB)
Installing collected packages: python-dotenv
Successfully installed python-dotenv-1.0.0

教學文件參考 :


使用方式很簡單, 同樣也是要先在工作目錄下準備一個檔名為 .env 的純文字隱藏檔設定環境變數的 key-value 對 (參考上面做法), 然後從 dotenv 套件載入 load_dotenv() 函式 : 

from dotenv import load_dotenv

接著呼叫 load_dotenv() 函式, 它會從 .env 檔讀取 key-value 對並將其設為環境變數, 設定成功會傳回 True, 例如 : 

>>> from dotenv import load_dotenv   
>>> load_dotenv()     
True   

然後匯入內建模組 os, 呼叫 os.environ.get() 函式並傳入環境變數名稱即可取得其值, 例如 :

>>> import os   
>>> os.environ.get('LINE_NOTIFY_TOKEN')     
'ud7PaDL45fz849A0e1f5oaMCbRIkxMXapQCt7PfNkzz'   

不過要注意的是, 如果工作目錄使用 Git 備份到例如 GitHub 等遠端寄存庫 (repository), 則這個 .env 檔也會被上傳, 若寄存庫是公開的, 那 .env 檔內的金鑰或權杖也會被無意中公開, 這可以透過 將 .env 寫入 .gitignore 檔中來防止, 當使用 git commit 指令進行提交時就會跳過 ,env 檔. 參考 :



3. 用環境變數儲存 Line Notify 權杖 : 

以下使用 python-dotenv 套件來載入透過 ,env 檔設定的 Line Notify 權杖環境變數 : 

# dotenv_test.py
import os  
import socket
import requests
from dotenv import load_dotenv   

def line_notify(msg, token):
    url="https://notify-api.line.me/api/notify"
    headers={"Authorization": "Bearer " + token,
             "Content-Type": "application/x-www-form-urlencoded"
             }
    payload={"message": msg}
    r=requests.post(url, headers=headers, params=payload)
    return r.status_code

if __name__ == '__main__':
    load_dotenv()                # 載入環境變數
    msg='dotenv 測試'
    token=os.environ.get('LINE_NOTIFY_TOKEN')    # 取得 Line Notify 權杖
    code=line_notify(msg, token)
    if code==200:
        print('Line 訊息發送成功!')
    else:
        print('Line 訊息發送失敗!')    

此例使用 python-dotenv 套件的 load_dotenv 來載入環境變數, 然後透過 os.environ.get() 取得 Line Notify 的權杖, 最後傳給 line_notify() 函式發送訊息, 結果如下 : 



沒有留言 :