å®å šãªããŒã¿éä¿¡ãšæ€èšŒã®ããã® HMAC å ¥éããå¿çšãŸã§
ã¯ããã«ïŒHMAC ãšã¯äœãïŒ ãªãéèŠãªã®ãïŒ ð€
ã€ã³ã¿ãŒãããäžã§ããŒã¿ãããåãããéãããã®ããŒã¿ã¯æ¬åœã«ä¿¡é Œã§ããéä¿¡å ããéãããŠãããã®ãïŒããéäžã§æ¹ãããããŠããªããïŒããšãã£ãçåã¯åžžã«ä»ããŸãšããŸããç¹ã«ãAPI ããŒããã¹ã¯ãŒãã®ãããªæ©å¯æ å ±ããããã¯éèŠãªååŒããŒã¿ãªã©ãæ±ãå Žåããã®ããŒã¿ã®å®å šæ§ (Integrity) ãšèªèšŒ (Authentication) ãä¿èšŒããããšã¯éåžžã«éèŠã§ãã
ããã§ç»å Žããã®ã HMAC (Hash-based Message Authentication Code) ã§ããHMAC ã¯ãå ±æç§å¯éµãšããã·ã¥é¢æ°ãçšããŠã¡ãã»ãŒãžèªèšŒã³ãŒããçæããä»çµã¿ã§ããããã«ããã以äžã®äºã€ã®éèŠãªç®çãéæã§ããŸãã
- ããŒã¿å®å šæ§ã®æ€èšŒ: ã¡ãã»ãŒãžãéä¿¡äžã«æ¹ãããããŠããªãããšã確èªã§ããŸãã
- éä¿¡å ã®èªèšŒ: ã¡ãã»ãŒãžããå ±æç§å¯éµãç¥ã£ãŠããæ£åœãªéä¿¡è ããéããããã®ã§ããããšã確èªã§ããŸããïŒãã ããHMACã ãã§ã¯éä¿¡è ã誰ã§ããããç¹å®ããããã§ã¯ãªãããéµãç¥ã£ãŠãã誰ããã§ããããšãä¿èšŒããŸãïŒ
Python ã§ã¯ããã® HMAC ãç°¡åã«å®è£
ããããã®æšæºã©ã€ãã©ãªãšã㊠hmac
ã¢ãžã¥ãŒã«ãæäŸãããŠããŸãããã®ããã°èšäºã§ã¯ãhmac
ã¢ãžã¥ãŒã«ã®åºæ¬çãªäœ¿ãæ¹ãããã»ãã¥ãªãã£äžã®æ³šæç¹ãå¿çšäŸãŸã§ãæ·±ãæãäžããŠè§£èª¬ããŠãããŸããWeb éçºãAPI èšèšãã»ãã¥ã¢ãªã·ã¹ãã æ§ç¯ã«é¢ãããã¹ãŠã®æ¹ã«ãšã£ãŠå¿
èªã®å
容ã§ãããããHMAC ã®äžçãžé£ã³èŸŒã¿ãŸãããïŒð
HMAC ã®ä»çµã¿ïŒããã·ã¥é¢æ°ãšç§å¯éµã®éæ³ ð§ââïž
HMAC ã®è©³çŽ°ãªã¢ã«ãŽãªãºã (RFC 2104 ã§å®çŸ©) ã«ç«ã¡å ¥ãåã«ããã®åºæ¬çãªæŠå¿µãç解ããŸããããHMAC ã¯ã以äžã®äžã€ã®èŠçŽ ããæ§æãããŸãã
- ã¡ãã»ãŒãž (Message): èªèšŒãããããŒã¿æ¬äœã§ããä»»æã®é·ãã®ãã€ãåã§ãã
- ç§å¯éµ (Secret Key): éä¿¡è ãšåä¿¡è ã ããç¥ã£ãŠããç§å¯ã®æååïŒãã€ãåïŒã§ããð ãã®éµã®æ©å¯æ§ã HMAC ã®å®å šæ§ã®æ ¹å¹¹ããªããŸãã
- ããã·ã¥é¢æ° (Cryptographic Hash Function): äŸãã° SHA-256 ã SHA-512 ã®ãããªãå ¥åããŒã¿ããåºå®é·ã®ããã·ã¥å€ãçæããé¢æ°ã§ããããã·ã¥é¢æ°ã¯äžæ¹åæ§ïŒããã·ã¥å€ããå ã®ããŒã¿ãæšæž¬ã§ããªãïŒãšè¡çªèæ§ïŒç°ãªãããŒã¿ããåãããã·ã¥å€ãçæããã«ããïŒãæã€å¿ èŠããããŸãã
HMAC ã®çæããã»ã¹ãç°¡åã«èª¬æãããšã以äžã®ããã«ãªããŸãïŒå®éã®ã¢ã«ãŽãªãºã ã¯ããå°ãè€éã§ãïŒã
HMAC çæã®ã€ã¡ãŒãž:
- ç§å¯éµãšã¡ãã»ãŒãžãç¹å®ã®ã«ãŒã«ã§çµã¿åãããã
- çµã¿åãããçµæããæå®ãããããã·ã¥é¢æ°ã«ãããã
- åŸãããããã·ã¥å€ã HMAC ãšãªãã
ããæ£ç¢ºã«ã¯ãRFC 2104 ã§ã¯ãç§å¯éµãå
éšããã (ipad) ãšå€éšããã (opad) ãš XOR ãããããã¡ãã»ãŒãžãšçµã¿åãããŠäºæ®µéã®ããã·ã¥èšç®ãè¡ããŸããããã«ãããåçŽãª hash(key + message)
ã hash(message + key)
ãšãã£ãæ¹åŒãããé«ãã»ãã¥ãªãã£ãå®çŸããŠããŸãã
åä¿¡åŽã§ã¯ãåãç§å¯éµãåãã¡ãã»ãŒãžãåãããã·ã¥é¢æ°ã䜿ã£ãŠ HMAC ãèšç®ããéä¿¡ãããŠãã HMAC ãšæ¯èŒããŸããããäºã€ã® HMAC ãäžèŽããã°ãã¡ãã»ãŒãžã¯æ¹ãããããŠãããããã€ããã®ã¡ãã»ãŒãžã¯ç§å¯éµãç¥ã£ãŠããïŒïŒæ£åœãªïŒéä¿¡è ããéãããå¯èœæ§ãé«ããšå€æã§ããŸããâ
Python `hmac` ã¢ãžã¥ãŒã«ã®åºæ¬ïŒæåã®ã¹ããã ð£
ããã§ã¯ãPython ã® hmac
ã¢ãžã¥ãŒã«ã䜿ã£ãŠã¿ãŸãããããã®ã¢ãžã¥ãŒã«ã¯æšæºã©ã€ãã©ãªã«å«ãŸããŠãããããå¥éã€ã³ã¹ããŒã«ããå¿
èŠã¯ãããŸããã
ã€ã³ããŒã
ãŸãã¯ã¢ãžã¥ãŒã«ãã€ã³ããŒãããŸããéåžžãããã·ã¥é¢æ°ãæäŸããããã« hashlib
ã¢ãžã¥ãŒã«ãäžç·ã«ã€ã³ããŒãããŸãã
import hmac
import hashlib
HMAC ãªããžã§ã¯ãã®çæïŒ `hmac.new()`
HMAC ãèšç®ããã«ã¯ãhmac.new()
é¢æ°ã䜿ã£ãŠ HMAC ãªããžã§ã¯ããäœæããŸãããã®é¢æ°ã¯ããã€ãã®éèŠãªåŒæ°ãåããŸãã
hmac_obj = hmac.new(key, msg=None, digestmod='')
key
: ç§å¯éµãæå®ããŸãããã€ãå (bytes) ã§ããå¿ èŠããããŸããæååã®å Žåã¯ãé©åãªãšã³ã³ãŒãã£ã³ã°ïŒäŸ: UTF-8ïŒã§ãã€ãåã«å€æããŠãã ãããðmsg
: èªèšŒããã¡ãã»ãŒãžãæå®ããŸããããããã€ãå (bytes) ã§ããå¿ èŠããããŸããçç¥å¯èœã§ãåŸè¿°ããupdate()
ã¡ãœããã§ã¡ãã»ãŒãžãè¿œå ããããšãã§ããŸããdigestmod
: 䜿çšããããã·ã¥ã¢ã«ãŽãªãºã ãæå®ããŸããhashlib
ã¢ãžã¥ãŒã«ã§ãµããŒããããŠããã¢ã«ãŽãªãºã ã®ååïŒæååãäŸ:'sha256'
ïŒãæå®ããããhashlib.sha256
ã®ãããªã³ã³ã¹ãã©ã¯ã¿/é¢æ°ãªããžã§ã¯ããæå®ããŸããã»ãã¥ãªãã£ã®èŠ³ç¹ãããçŸåšã¯ SHA-256 ä»¥äž ã®å©çšã匷ãæšå¥šãããŸããå€ãã¢ã«ãŽãªãºã ïŒMD5, SHA-1ïŒã¯è匱æ§ãææãããŠãããé¿ããã¹ãã§ããð«
ãã€ãžã§ã¹ãïŒHMAC å€ïŒã®ååŸ
HMAC ãªããžã§ã¯ããäœæãããã以äžã®ã¡ãœããã§èšç®çµæïŒãã€ãžã§ã¹ãïŒãååŸã§ããŸãã
digest()
: èšç®ããã HMAC å€ããã€ãå (bytes) ãšããŠè¿ããŸãããã€ããªããŒã¿ã®ãŸãŸæ±ãããå Žåã«äœ¿çšããŸããhexdigest()
: èšç®ããã HMAC å€ã16é²æ°æååãšããŠè¿ããŸãã人éãèªã¿ããã圢åŒããHTTP ããããŒãªã©ã§ããã¹ããšããŠæ±ãããå Žåã«äŸ¿å©ã§ãã
ç°¡åãªäŸ
å®éã« HMAC-SHA256 ãèšç®ããŠã¿ãŸãããã
import hmac
import hashlib
# ç§å¯éµ (ãã€ãå)
secret_key = b'mysecretkey' # å®éã«ã¯ãã£ãšè€éã§ã©ã³ãã ãªéµã䜿çšããŠãã ããïŒ
# ã¡ãã»ãŒãž (ãã€ãå)
message = b'This is the message to authenticate'
# HMAC-SHA256 ãªããžã§ã¯ããäœæ
# digestmod ã«ã¯ hashlib.sha256 ãæå®ããã®ãäžè¬ç
h = hmac.new(secret_key, message, hashlib.sha256)
# HMAC å€ããã€ãåã§ååŸ
digest_bytes = h.digest()
print(f"HMAC (bytes): {digest_bytes}")
# HMAC å€ã16é²æ°æååã§ååŸ
digest_hex = h.hexdigest()
print(f"HMAC (hex): {digest_hex}")
# æååãããŒãã¡ãã»ãŒãžãšããŠäœ¿ãå Žåã¯ãšã³ã³ãŒããå¿
èŠ
string_key = "mysecretkey_as_string"
string_message = "ã¡ãã»ãŒãžã¯æååã§ãOKïŒãã ããã€ãåã«å€æïŒ"
h_str = hmac.new(
string_key.encode('utf-8'), # UTF-8 ã§ãã€ãåã«å€æ
string_message.encode('utf-8'), # UTF-8 ã§ãã€ãåã«å€æ
hashlib.sha256
)
print(f"HMAC (hex, from strings): {h_str.hexdigest()}")
泚æç¹:
key
ãšmsg
ã¯å¿ ããã€ãå (bytes) ã§æž¡ãå¿ èŠããããŸããæååã䜿çšããå Žåã¯ã.encode('utf-8')
ãªã©ã§é©åã«ãšã³ã³ãŒãããŠãã ããããšã³ã³ãŒãã£ã³ã°ãç°ãªããš HMAC å€ãå€ãã£ãŠããŸããããéåä¿¡è éã§ãšã³ã³ãŒãã£ã³ã°ãçµ±äžããããšãéèŠã§ãã- ç§å¯éµã¯çµ¶å¯Ÿã«ããŒãã³ãŒãã£ã³ã°ãããç°å¢å€æ°ãèšå®ãã¡ã€ã«ãã·ãŒã¯ã¬ãã管çã·ã¹ãã ãªã©ãå®å
šãªæ¹æ³ã§ç®¡çããŠãã ããããã®äŸã®
b'mysecretkey'
ã¯ãããŸã§ãã¢ã³ã¹ãã¬ãŒã·ã§ã³çšã§ãã
ããã·ã¥ã¢ã«ãŽãªãºã ã®éžæïŒåŒ·åºŠã®èŠ ðª
HMAC ã®å®å
šæ§ã¯ã䜿çšããããã·ã¥ã¢ã«ãŽãªãºã ã®åŒ·åºŠã«å€§ããäŸåããŸããhmac
ã¢ãžã¥ãŒã«ã¯ hashlib
ã¢ãžã¥ãŒã«ãšé£æºããŠãããhashlib
ããµããŒãããå€ãã®ã¢ã«ãŽãªãºã ãå©çšã§ããŸãã
å©çšå¯èœãªã¢ã«ãŽãªãºã ã¯ãã䜿ãã® Python ç°å¢ïŒããã³ OpenSSL ã©ã€ãã©ãªïŒã«ãã£ãŠç°ãªããŸãããäžè¬çã«ã¯ä»¥äžã®ãããªãã®ãå«ãŸããŸãã
ã¢ã«ãŽãªãºã å (digestmod) | æšå¥šåºŠ | äž»ãªç¹åŸŽ |
---|---|---|
'sha256' / hashlib.sha256 |
æšå¥š | çŸåšãæãåºã䜿ãããŠããæšæºçãªéžæè¢ãååãªåŒ·åºŠãæã€ã |
'sha384' / hashlib.sha384 |
æšå¥š | SHA-256 ãããé«ã匷床ãå¿ èŠãªå Žåã«ã |
'sha512' / hashlib.sha512 |
æšå¥š | SHA-256 ãããé«ã匷床ãå¿ èŠãªå Žåã«ã64bit ç°å¢ã§ã¯ SHA-256 ããé«éãªå Žåãããã |
'sha3_256' , 'sha3_512' ãªã© |
æ€èšå¯ | SHA-3 ãã¡ããªãŒãSHA-2 ãšã¯ç°ãªãå éšæ§é ãæã€æ°ããæšæºã |
'blake2b' , 'blake2s' |
æ€èšå¯ | é«ãããã©ãŒãã³ã¹ãšã»ãã¥ãªãã£ãäž¡ç«ããã¢ãã³ãªããã·ã¥é¢æ°ã |
'sha1' / hashlib.sha1 |
éæšå¥š ð« | è¡çªæ»æã«å¯Ÿããè匱æ§ãçºèŠãããŠããŸã (2017幎 Googleã«ããSHAtteredæ»æ)ãæ°èŠã·ã¹ãã ã§ã®å©çšã¯é¿ããã¹ãã§ãã |
'md5' / hashlib.md5 |
éæšå¥š ð« | å€ããã䜿ãããŠããŸãããè¡çªèæ§ãèããäœããã»ãã¥ãªãã£çšéã«ã¯çµ¶å¯Ÿã«äœ¿çšããªãã§ãã ããã |
å©çšå¯èœãªã¢ã«ãŽãªãºã ã®äžèŠ§ã¯ãhashlib.algorithms_guaranteed
ïŒåžžã«å©çšå¯èœãªãã®ïŒã hashlib.algorithms_available
ïŒOpenSSL ãæäŸãããã®ãå«ãïŒã§ç¢ºèªã§ããŸãã
import hashlib
print("Guaranteed algorithms:", hashlib.algorithms_guaranteed)
print("Available algorithms:", hashlib.algorithms_available)
çµè«ãšããŠãç¹å¥ãªçç±ããªãéããHMAC ã«ã¯ SHA-256 ã䜿çšããããšã匷ããå§ãããŸãã è¿·ã£ãã SHA-256 ãéžãã§ããã°ãå€ãã®å Žåãé©åãªã»ãã¥ãªãã£ã¬ãã«ã確ä¿ã§ããŸãã
å®è·µçãªäœ¿ãæ¹ïŒããŒã¿ã¹ããªãŒã ãšæ®µéçåŠç ð»
hmac.new()
ã® msg
åŒæ°ã«ãã¹ãŠã®ã¡ãã»ãŒãžãäžåºŠã«æž¡ãã ãã§ãªãã倧ããªããŒã¿ãã¹ããªãŒã ããŒã¿ãæ±ãå Žåã«ã¯ãupdate()
ã¡ãœããã䜿ã£ãŠæ®µéçã«ã¡ãã»ãŒãžãè¿œå ããŠããããšãã§ããŸãã
`update()` ã¡ãœãã
update()
ã¡ãœããã¯ãHMAC ãªããžã§ã¯ãã«ã¡ãã»ãŒãžã®ãã£ã³ã¯ïŒéšåïŒãè¿œå ããŸãããã®ã¡ãœããã¯è€æ°ååŒã³åºãããšãã§ããHMAC ã¯å
éšçã«ç¶æ
ãä¿æããŠãæçµçã«ãã¹ãŠã®ãã£ã³ã¯ãé£çµããã¡ãã»ãŒãžã«å¯Ÿãã HMAC å€ãèšç®ããŸãã
import hmac
import hashlib
secret_key = b'another_secure_key'
h = hmac.new(secret_key, digestmod=hashlib.sha256) # msg ã¯æåã¯æå®ããªã
# ã¡ãã»ãŒãžããã£ã³ã¯ã«åã㊠update() ã§è¿œå
chunk1 = b'This is the first part '
chunk2 = b'of a longer message.'
chunk3 = b' Processed in chunks.'
h.update(chunk1)
h.update(chunk2)
h.update(chunk3)
# ãã¹ãŠã®ãã£ã³ã¯ãåŠçãããåŸã§ãã€ãžã§ã¹ããååŸ
final_hmac_hex = h.hexdigest()
print(f"HMAC (from chunks): {final_hmac_hex}")
# åèïŒäžåºŠã«å
šã¡ãã»ãŒãžãæž¡ããå Žåãšåãçµæã«ãªãããšã確èª
full_message = chunk1 + chunk2 + chunk3
h_full = hmac.new(secret_key, full_message, hashlib.sha256)
print(f"HMAC (full message): {h_full.hexdigest()}")
print(f"Are they equal? {final_hmac_hex == h_full.hexdigest()}") # True ã«ãªãã¯ã
ãã® update()
ã¡ãœããã¯ã以äžã®ãããªå Žåã«ç¹ã«åœ¹ç«ã¡ãŸãã
- ãã¡ã€ã«ã®å å®¹å šäœãã¡ã¢ãªã«èªã¿èŸŒãŸãã« HMAC ãèšç®ãããå Žåã
- ãããã¯ãŒã¯ããããŒã¿ãå°ããã€åä¿¡ããªãã HMAC ãèšç®ãããå Žåã
äŸãã°ããã¡ã€ã«ã® HMAC ãèšç®ããäŸã§ãã
import hmac
import hashlib
import os # ãããŒãã¡ã€ã«äœæçš
def calculate_hmac_for_file(filepath, key, digestmod=hashlib.sha256, chunk_size=4096):
"""ãã¡ã€ã«ã® HMAC-SHA256 ãèšç®ãã"""
h = hmac.new(key, digestmod=digestmod)
try:
with open(filepath, 'rb') as f: # ãã€ããªã¢ãŒãã§éã
while True:
chunk = f.read(chunk_size)
if not chunk:
break
h.update(chunk)
return h.hexdigest()
except FileNotFoundError:
print(f"Error: File not found at {filepath}")
return None
except Exception as e:
print(f"An error occurred: {e}")
return None
# ãããŒãã¡ã€ã«ã®äœæ (å®è¡åã«åé€ãããããã«é
æ
®)
dummy_filename = "my_large_file.dat"
try:
with open(dummy_filename, "wb") as f:
f.write(os.urandom(1024 * 1024)) # 1MB ã®ã©ã³ãã ããŒã¿
secret_key = os.urandom(32) # å®éã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯å®å
šã«ç®¡çãããéµã䜿çš
file_hmac = calculate_hmac_for_file(dummy_filename, secret_key)
if file_hmac:
print(f"HMAC for {dummy_filename}: {file_hmac}")
finally:
# ãããŒãã¡ã€ã«ãåé€
if os.path.exists(dummy_filename):
os.remove(dummy_filename)
print(f"Dummy file {dummy_filename} removed.")
ã»ãã¥ãªãã£äžã®èæ ®äºé ïŒå®å šãª HMAC ã®éçš ðð¡ïž
HMAC ã¯åŒ·åãªããŒã«ã§ããããã®å®å šæ§ãæ倧éã«åŒãåºãããã«ã¯ãããã€ãã®éèŠãªç¹ã«æ³šæããå¿ èŠããããŸãã
1. ç§å¯éµã®ç®¡çïŒæéèŠèª²é¡ïŒ ð
- æ©å¯æ§: ç§å¯éµã¯çµ¶å¯Ÿã«æŒæŽ©ãããŠã¯ãããŸããããœãŒã¹ã³ãŒããžã®ããŒãã³ãŒãã£ã³ã°ã¯å³çŠã§ããç°å¢å€æ°ãèšå®ãã¡ã€ã«ïŒé©åãªããŒããã·ã§ã³èšå®ãå¿ èŠïŒãKubernetes SecretsãHashiCorp Vault ãªã©ã®ã·ãŒã¯ã¬ãã管çã·ã¹ãã ãå©çšããŠãå®å šã«ä¿ç®¡ã»é åžããŠãã ããã
- ã©ã³ãã æ§: éµã¯äºæž¬å°é£ã§ããå¿
èŠããããŸããåçŽãªåèªãçããã¬ãŒãºã§ã¯ãªããæå·è«çã«å®å
šãªä¹±æ°çæåšïŒäŸ:
os.urandom()
ïŒã䜿ã£ãŠçæãããååãªé·ãã®ã©ã³ãã ãªãã€ãåã䜿çšããŠãã ããã - é·ã: éµã®é·ãã¯ã䜿çšããããã·ã¥é¢æ°ã®åºåé·ïŒãŸãã¯ãããã¯é·ïŒãšåçšåºŠä»¥äžãæšå¥šãããŸããäŸãã°ãHMAC-SHA256 ã®å Žåã32 ãã€ã (256 ããã) 以äžã®é·ããæã€éµãæãŸããã§ããçãããéµã¯ãç·åœããæ»æã«å¯ŸããŠè匱ã«ãªãå¯èœæ§ããããŸãã
- å®æçãªå€æŽ: å¯èœã§ããã°ãå®æçã«éµãããŒããŒã·ã§ã³ïŒå€æŽïŒããéçšãã»ãã¥ãªãã£ãé«ããŸãã
import os
# å®å
šãªéµã®çæäŸ (32ãã€ã = 256ããã)
secure_key = os.urandom(32)
print(f"Generated secure key (bytes): {secure_key}")
print(f"Generated secure key (hex): {secure_key.hex()}")
# 泚æ: ãã®çæãããéµãå®å
šãªå Žæã«ä¿ç®¡ããã¢ããªã±ãŒã·ã§ã³ããèªã¿èŸŒãããã«ããŠãã ããã
# äŸãã°ç°å¢å€æ°ããèªã¿èŸŒãå Žå:
# import os
# secret_key_from_env = os.environ.get('MY_APP_HMAC_SECRET_KEY')
# if not secret_key_from_env:
# raise ValueError("HMAC secret key not found in environment variables!")
# # ç°å¢å€æ°ããèªã¿èŸŒãã æååããã€ãåã«å€æããå¿
èŠãããå Žåããã
# # (äŸ: Base64ãšã³ã³ãŒããããŠããå Žåãªã©ãä¿åæ¹æ³ã«ãã)
# # ããã§ã¯ä»®ã« UTF-8 ã§ãšã³ã³ãŒããããæååã ãšä»®å®
# secret_key_bytes = secret_key_from_env.encode('utf-8') # ãŸã㯠Base64 ãã³ãŒããªã©
2. ã¿ã€ãã³ã°æ»æãš `compare_digest()` ã®éèŠæ§ â±ïžð¥
HMAC ãåä¿¡åŽã§æ€èšŒããéãèšç®ãã HMAC ãšåä¿¡ãã HMAC ãæ¯èŒããå¿
èŠããããŸããããã§ãåçŽãªæååæ¯èŒæŒç®å (==
) ã䜿ãã®ã¯éåžžã«å±éºã§ãããªããªãã==
ã«ããæ¯èŒã¯ãæååã®å
é ããäžæåãã€æ¯èŒããç°ãªãæåãèŠã€ãã£ãæç¹ã§False
ãè¿ããŠåŠçãçµäºããããã§ãã
ãã®åäœã¯ãæ»æè ãæ¯èŒã«ãããæéã枬å®ããããšã§ãHMAC å€ãå°ããã€æšæž¬ã§ããŠããŸããã¿ã€ãã³ã°æ»æ (Timing Attack)ããšåŒã°ãããµã€ããã£ãã«æ»æãå¯èœã«ããŸããäŸãã°ãæ»æè ãéã£ã HMAC ã®æåã®1æåãæ£ãããã°ãæ¯èŒã«ãããã«æéãããããŸãã2æåç®ãæ£ãããã°ãããã«æéãããããŸãããã®åŸ®çŽ°ãªæéå·®ãå€æ°å枬å®ããããšã§ãç§å¯éµãç¥ããªããŠãæå¹ãª HMAC ãæšæž¬ã§ããŠããŸãå¯èœæ§ãããã®ã§ããð±
ãã®åé¡ãåé¿ããããã«ãPython ã® hmac
ã¢ãžã¥ãŒã«ã«ã¯ compare_digest()
ãšããé¢æ°ãçšæãããŠããŸãã
hmac.compare_digest(a, b)
a
,b
: æ¯èŒãããäºã€ã®ãã€ãžã§ã¹ãïŒãã€ãåãŸã㯠ASCII æååïŒã- ãã®é¢æ°ã¯ãäºã€ã®å
¥åã®é·ããç°ãªãå Žåã¯ããã«
False
ãè¿ããŸãã - é·ããåãå Žåãå ¥åã®å 容ã«é¢ããããåžžã«äžå®æéã§æ¯èŒåŠçãè¡ããŸãïŒå°éçã«ã¯ãã³ã³ã¹ã¿ã³ãã¿ã€ã æ¯èŒããšåŒã°ããŸãïŒãããã«ãããåŠçæéã®å·®ããæ å ±ãæšæž¬ããã¿ã€ãã³ã°æ»æãé²ããŸããð¡ïž
HMAC ã®æ¯èŒã«ã¯ãå¿
ã hmac.compare_digest()
ã䜿çšããŠãã ããã
import hmac
import hashlib
import os
import time # ã¿ã€ãã³ã°æ¯èŒã®ãã¢çš (å®éã«ã¯åŸ®çŽ°ãªå·®)
secret_key = os.urandom(32)
message = b"Validate me securely!"
# æ£ãã HMAC ãèšç®
correct_hmac = hmac.new(secret_key, message, hashlib.sha256).hexdigest()
# åä¿¡ãã (ãããããªã) HMAC
received_hmac_correct = correct_hmac
received_hmac_incorrect = "a" * len(correct_hmac) # ééã£ã HMAC (é·ãã¯åã)
received_hmac_short = "abc" # ééã£ã HMAC (é·ããéã)
# --- ãã£ãŠã¯ãããªãæ¯èŒ (ã¿ã€ãã³ã°æ»æã«è匱) ---
start_time = time.perf_counter_ns()
result_eq_correct = (correct_hmac == received_hmac_correct)
end_time = time.perf_counter_ns()
print(f"Using '==': Correct HMAC comparison result: {result_eq_correct}, time: {end_time - start_time} ns (example)")
start_time = time.perf_counter_ns()
result_eq_incorrect = (correct_hmac == received_hmac_incorrect)
end_time = time.perf_counter_ns()
# 泚æ: ãã®æéå·®ã¯éåžžéåžžã«å°ããããããã¯ãŒã¯é
延ãªã©ä»ã®èŠå ã®åœ±é¿ã倧ããã
# ã¿ã€ãã³ã°æ»æã¯çµ±èšçã«å€æ°ã®è©Šè¡ãå¿
èŠãšããã
print(f"Using '==': Incorrect HMAC comparison result: {result_eq_incorrect}, time: {end_time - start_time} ns (example)")
# --- å®å
šãªæ¯èŒ (compare_digest ã䜿çš) ---
# compare_digest ã¯ãã€ãåãŸã㯠ASCII æååãæ³å®ãããããhexdigest() ã®çµæããã®ãŸãŸäœ¿ãã
# (ãã ããå¯èœã§ããã° digest() ã®çµæ (ãã€ãå) ãçŽæ¥æ¯èŒããã®ãããäžè¬ç)
correct_hmac_bytes = hmac.new(secret_key, message, hashlib.sha256).digest()
received_hmac_correct_bytes = correct_hmac_bytes
received_hmac_incorrect_bytes = b"x" * len(correct_hmac_bytes)
start_time = time.perf_counter_ns()
result_cd_correct = hmac.compare_digest(correct_hmac_bytes, received_hmac_correct_bytes)
end_time = time.perf_counter_ns()
print(f"Using compare_digest: Correct HMAC comparison result: {result_cd_correct}, time: {end_time - start_time} ns (example)")
start_time = time.perf_counter_ns()
result_cd_incorrect = hmac.compare_digest(correct_hmac_bytes, received_hmac_incorrect_bytes)
end_time = time.perf_counter_ns()
print(f"Using compare_digest: Incorrect HMAC comparison result: {result_cd_incorrect}, time: {end_time - start_time} ns (example)")
start_time = time.perf_counter_ns()
result_cd_short = hmac.compare_digest(correct_hmac_bytes, received_hmac_short.encode()) # é·ããéãå Žå
end_time = time.perf_counter_ns()
print(f"Using compare_digest: Short HMAC comparison result: {result_cd_short}, time: {end_time - start_time} ns (example)")
# æåå (hexdigest) ã§æ¯èŒããå Žåãåæ§
# compare_digest ã¯å
éšã§ãã€ãåã«å€æããŠæ¯èŒãããããASCII æååãªãOK
# ãã ããéASCIIæåãå«ãå¯èœæ§ãããå Žå㯠.encode() ããã®ãå®å
š
hmac.compare_digest(correct_hmac, received_hmac_correct) # OK
hmac.compare_digest(correct_hmac, received_hmac_incorrect) # OK
éèŠ: compare_digest()
㯠Python 2.7.7+ ããã³ 3.3+ ã§å©çšå¯èœã§ããå€ãããŒãžã§ã³ã® Python ã䜿çšããŠããå Žåã¯ããã®é¢æ°ãååšããªããããã¢ããã°ã¬ãŒããããããµãŒãããŒãã£ã®ã©ã€ãã©ãªïŒäŸ: django.utils.crypto.constant_time_compare
ãªã©ïŒã䜿çšããå¿
èŠããããŸãã
3. ããã·ã¥ã¢ã«ãŽãªãºã ã®éžæ
åè¿°ã®éããMD5 ã SHA-1 ã®ãããªå€ãããã·ã¥ã¢ã«ãŽãªãºã ã¯æ¢ç¥ã®è匱æ§ãæã£ãŠããŸããHMAC ã§äœ¿çšããå ŽåãçŽç²ãªããã·ã¥é¢æ°ãšããŠã®è¡çªæ»æãçŽæ¥ HMAC ã®ç Žç¶»ã«ã€ãªããããã§ã¯ãããŸããããããå®å šãª SHA-2 ãã¡ããªãŒ (SHA-256, SHA-512 ãªã©) ã SHA-3 ã䜿çšããããšã匷ãæšå¥šãããŸããã¢ã«ãŽãªãºã ã®éžæã¯ãã¢ããªã±ãŒã·ã§ã³ã®ã»ãã¥ãªãã£èŠä»¶ãæºæ ãã¹ãæšæºã«åºã¥ããŠæ±ºå®ããŠãã ããã
4. ã¡ãã»ãŒãžã®å 容
HMAC ã¯ã¡ãã»ãŒãžã®å 容ãæ¹ãããããŠããªãããšãä¿èšŒããŸãããã¡ãã»ãŒãžã®å 容èªäœã®æ©å¯æ§ïŒæå·åïŒã¯æäŸããŸãããHMAC ãããã¡ãã»ãŒãžããããã¯ãŒã¯äžã§çèŽãããå Žåãå 容ã¯èªã¿åãããŠããŸããŸããæ©å¯æ§ãå¿ èŠãªå Žåã¯ãHMAC ãšã¯å¥ã«ãTLS/SSL ã AES ãªã©ã®æå·åæè¡ã䜵çšããå¿ èŠããããŸãã
ãŸããHMAC ã®èšç®ã«ã¯ãèªèšŒãããå šãŠã®é¢é£æ å ±ãå«ããããšãéèŠã§ããäŸãã°ãWeb API ã®ãªã¯ãšã¹ããèªèšŒããå Žåããªã¯ãšã¹ãããã£ã ãã§ãªããHTTP ã¡ãœããããªã¯ãšã¹ããã¹ãã¿ã€ã ã¹ã¿ã³ãããã³ã¹ïŒäžåºŠãã䜿ãããªãã©ã³ãã ãªå€ïŒãªã©ãå«ã㊠HMAC ãèšç®ããããšã§ããªãã¬ã€æ»æïŒäžåºŠååãããªã¯ãšã¹ããåéããæ»æïŒãªã©ãé²ãããšãã§ããŸããäœãã¡ãã»ãŒãžã«å«ãããã¯ãã»ãã¥ãªãã£èšèšã®éèŠãªèŠçŽ ã§ãã
å¿çšäŸïŒã©ã㧠HMAC ã掻èºãããïŒ ð¡
HMAC ã¯ãæ§ã ãªã»ãã¥ãªãã£é¢é£ã®ã·ããªãªã§å©çšãããŠããŸãã
-
API ãªã¯ãšã¹ãã®çœ²å:
å€ãã® Web API (äŸãã° AWS ã® API Gateway, Stripe API ãªã©) ã§ã¯ããªã¯ãšã¹ããæ£åœãªã¯ã©ã€ã¢ã³ãããéä¿¡ãããæ¹ãããããŠããªãããšã確èªããããã« HMAC ã䜿çšããŠããŸããã¯ã©ã€ã¢ã³ãã¯ããªã¯ãšã¹ãã®å
容ïŒã¡ãœããããã¹ãã¯ãšãªãã©ã¡ãŒã¿ãããã£ãã¿ã€ã ã¹ã¿ã³ããªã©ïŒãšç§å¯éµïŒAPI ã·ãŒã¯ã¬ããããŒïŒã䜿ã£ãŠ HMAC ãèšç®ããããã HTTP ããããŒïŒäŸ:
Authorization
ãã«ã¹ã¿ã ããããŒX-Signature
ïŒã«å«ããŠéä¿¡ããŸãããµãŒããŒåŽã¯åãèšç®ãè¡ããHMAC ãæ€èšŒããŸããããã«ãããäžæ£ãªãªã¯ãšã¹ããæ¹ããããããªã¯ãšã¹ããæåŠã§ããŸãã - ã¡ãã»ãŒãžèªèšŒã³ãŒã (MAC): HMAC ã¯ãã®åã®éããã¡ãã»ãŒãžèªèšŒã³ãŒã (MAC) ã®äžçš®ã§ããæå·åãããã¡ãã»ãŒãžãšäžç·ã« HMAC ãéä¿¡ããããšã§ã埩å·åŸã«ã¡ãã»ãŒãžãæ¹ãããããŠããªããã確èªã§ããŸã (Encrypt-then-MAC ã¢ãããŒã)ã
- ã»ãã¥ã¢ãªããŒã¯ã³çæã»æ€èšŒ: JSON Web Token (JWT) ã®çœ²åã¢ã«ãŽãªãºã ã®äžã€ãšã㊠HMAC (HS256, HS384, HS512) ã䜿ãããŠããŸãããŸãããã¹ã¯ãŒããªã»ããããŒã¯ã³ãã»ãã·ã§ã³ ID ãªã©ãæ¹ãããããŠã¯å°ãäžæçãªããŒã¯ã³ã®çæãšæ€èšŒã«ãå©çšã§ããŸããããŒã¯ã³èªäœã«æå¹æéãªã©ã®æ å ±ãå«ããããå šäœã HMAC ã§çœ²åããããšã§ãããŒã¯ã³ã®æ¹ãããé²ããŸãã
- Webhook ã®æ€èšŒ: GitHub ã Stripe ãªã©ã®ãµãŒãã¹ãæäŸãã Webhook ã§ã¯ãéä¿¡ããããã€ããŒããæ¬åœã«ãã®ãµãŒãã¹ããéããããã®ã§ãããæ¹ãããããŠããªãããšã確èªããããã«ãHMAC 眲åã䜿ãããããšãäžè¬çã§ãããµãŒãã¹åŽã§èšå®ããã·ãŒã¯ã¬ãããšãã€ããŒãããèšç®ãã HMAC ãããªã¯ãšã¹ãããããŒã«å«ãŸãã眲åãšæ¯èŒããŸãã
-
ãã¹ã¯ãŒãããã·ã¥ (PBKDF2-HMAC):
ãã¹ã¯ãŒããå®å
šã«ä¿åããããã®ããŒæŽŸçé¢æ°ã§ãã PBKDF2 (Password-Based Key Derivation Function 2) ã§ã¯ãå
éšçã« HMAC ãç¹°ãè¿ãé©çšããããšã§ãç·åœããæ»æãèŸæžæ»æã«å¯Ÿããèæ§ãé«ããŠããŸãã
hashlib.pbkdf2_hmac()
é¢æ°ãšã㊠Python ã§ãå©çšå¯èœã§ãã
ãããã®äŸããããããããã«ãHMAC ã¯ããŒã¿ã®å®å šæ§ãšèªèšŒãæ±ããããå€ãã®å Žé¢ã§ãåºç€çãã€éèŠãªåœ¹å²ãæãããŠããŸãã
ãŸãšãïŒ`hmac` ã¢ãžã¥ãŒã«ã䜿ãããªããïŒ ð
Python ã® hmac
ã¢ãžã¥ãŒã«ã¯ãã¡ãã»ãŒãžã®å®å
šæ§ãšèªèšŒãä¿èšŒããããã®åŒ·åã§äœ¿ããããããŒã«ã§ãããã®åºæ¬ã¯ã·ã³ãã«ã§ãããå®å
šã«å©çšããããã«ã¯ä»¥äžã®ç¹ãåžžã«æèããå¿
èŠããããŸãã
- ð ç§å¯éµã®å³éãªç®¡ç: æãéèŠã§ããæŒæŽ©ããªããããå®å šãªæ¹æ³ã§ä¿ç®¡ã»é åžããŠãã ããã
- ð¡ïž ããã·ã¥ã¢ã«ãŽãªãºã ã®éžæ: SHA-256 以äžãæšå¥šããŸããMD5 ã SHA-1 ã¯é¿ããŸãããã
- â±ïž `compare_digest()` ã®äœ¿çš: HMAC ã®æ¯èŒã«ã¯å¿ ããã®é¢æ°ã䜿ããã¿ã€ãã³ã°æ»æãé²ããŸãããã
- Bytes in, Bytes out: ããŒãšã¡ãã»ãŒãžã¯ãã€ãåã§æ±ããŸããæååã®å Žåã¯ãšã³ã³ãŒãã£ã³ã°ã«æ³šæããŠãã ããã
- ð èªèšŒå¯Ÿè±¡ã®æ確å: äœãã¡ãã»ãŒãžãšã㊠HMAC èšç®ã«å«ãããããã»ãã¥ãªãã£ã¬ãã«ãå·Šå³ããŸãã
ãã®ããã°èšäºãéããŠãhmac
ã¢ãžã¥ãŒã«ã®ä»çµã¿ã䜿ãæ¹ããããŠå®å
šãªéçšæ¹æ³ã«ã€ããŠã®ç解ãæ·±ãŸã£ããªã幞ãã§ããWeb API ã®ã»ãã¥ãªãã£åŒ·åãããŒã¿æŽåæ§ã®ç¢ºä¿ãã»ãã¥ã¢ãªã·ã¹ãã æ§ç¯ãªã©ãæ§ã
ãªå Žé¢ã§ HMAC ã掻çšããããå®å
šãªã¢ããªã±ãŒã·ã§ã³éçºãç®æããŸãããïŒ ðªâš
ããã«è©³ããæ
å ±ãææ°ã®ä»æ§ã«ã€ããŠã¯ãPython ã®å
¬åŒããã¥ã¡ã³ããåç
§ããããšããå§ãããŸãã
Python å
¬åŒããã¥ã¡ã³ã – hmac
Python å
¬åŒããã¥ã¡ã³ã – hashlib
ã³ã¡ã³ã