Skip to content

Instantly share code, notes, and snippets.

@nikhilkumarsingh
Last active March 9, 2021 23:35
Show Gist options
  • Save nikhilkumarsingh/6d4b5ae222df2e5a4ae8c45192bb5c67 to your computer and use it in GitHub Desktop.
Save nikhilkumarsingh/6d4b5ae222df2e5a4ae8c45192bb5c67 to your computer and use it in GitHub Desktop.
Introduction to secrets in Python
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# secrets module in Python\n",
"\n",
"\n",
"![](https://i.imgur.com/BEoWiKz.png)\n",
"\n",
"> The secrets module is used for generating **cryptographically strong random numbers** suitable for managing data such as passwords, account authentication, security tokens, and related secrets.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Why not use `random` module?\n",
"\n",
"> A random number generator (RNG) is a device that generates a sequence of numbers or symbols that cannot be reasonably predicted better than by a random chance. Random number generators can be true hardware random-number generators (HRNG), which generate genuinely random numbers, or pseudo-random number generators (PRNG), which generate numbers that look random, but are actually deterministic, and can be reproduced if the state of the PRNG is known.\n",
"\n",
"- `random` module uses a **pseudo**-random number generator, [Mersenne Twister](https://en.wikipedia.org/wiki/Mersenne_Twister).\n",
"\n",
"- The PRNG-generated sequence is not truly random, because it is completely determined by an initial value, called the PRNG's **seed**.\n",
"\n",
"- Hence, the entire seemingly random sequence can be reproduced if the **seed** value is known.\n",
"\n",
"- Fast and reproducible\n",
"\n",
"- Good for simulation & modelling applications and games.\n",
"\n",
"- Not secure for cryptographic applications because next output is predictable from earlier outputs."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import random"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"random.seed(3)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[4, 10, 9, 3, 6, 10, 8, 10, 2, 10]\n"
]
}
],
"source": [
"print([random.randint(1,10) for _ in range(10)])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Python support for *hardware random-number generators* (HRNG)\n",
"\n",
"### How does HRNG work?\n",
"\n",
"- measures some physical phenomenon that is expected to be random\n",
"\n",
"- measures from sources of natural entropy, such as \n",
" - atmospheric noise, \n",
" - thermal noise, and\n",
" - other external electromagnetic and quantum phenomena.\n",
" \n",
"- they are rate-limited until enough entropy is harvested to meet the demand/ Until then, they are blocked.\n",
"\n",
"- Due to this blocking behavior, HRNGs can be slow.\n",
"\n",
"\n",
"----\n",
"In order to fetch truly random data from OS using Python: [os.urandom](https://docs.python.org/3/library/os.html#os.urandom)\n",
"\n",
"> If seed value is not provided explicitly, `random` module first tries to read `os.urandom` value for setting seed. If read fails, current time + process identifier is set as the seed. [Source](https://github.com/python/cpython/blob/master/Modules/_randommodule.c#L265-#L283)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"import os"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"b'\\xdeRk\\x7f'"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"os.urandom(4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`random` module also provides a random number generator built over `os.urandom`, called [`SystemRandom`](https://github.com/python/cpython/blob/3.8/Lib/random.py#L709-#L736)."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"srandom = random.SystemRandom()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[7, 3, 1, 7, 7, 2, 3, 8, 9, 2]\n"
]
}
],
"source": [
"print([srandom.randint(1,10) for _ in range(10)])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## [`secrets`](https://docs.python.org/3/library/secrets.html) methods\n",
"\n",
"### 1. `secrets.choice`"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"import secrets"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"secrets.choice([1,3,5,7])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2. `secrets.randbelow`"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"4"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"secrets.randbelow(5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3. `secrets.randbits`"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"783"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"secrets.randbits(10)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> The secrets module provides functions for generating secure tokens, suitable for applications such as password resets, hard-to-guess URLs, and similar.\n",
"\n",
"### 4. `secrets.token_bytes`"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"b'\\xc2W\\x07\\x8c-'"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"secrets.token_bytes(5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 5. `secrets.token_hex`"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'9fdd38ef6ea434dc778bafe1a48187f46abb194fd18352efb224efec8ef55e07'"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"secrets.token_hex()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 6. `secrets.token_urlsafe`\n",
"\n",
"[Source](https://github.com/python/cpython/blob/master/Lib/secrets.py#L61)\n",
"\n",
"[Base64 table](https://en.wikipedia.org/wiki/Base64#Base64_table)\n",
"\n",
"[base64.urlsafe_b64encode](https://github.com/python/cpython/blob/3.8/Lib/base64.py#L111-#L118)\n",
"\n",
"[How many bytes should tokens use?](https://docs.python.org/3/library/secrets.html#how-many-bytes-should-tokens-use)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'k1ANyx4ljvKWuQ'"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"secrets.token_urlsafe(10)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 7. `secrets.compare_digest`\n",
"\n",
"To reduce the risk of timing attacks.\n",
"\n",
"> A timing attack is a security exploit that allows an attacker to discover vulnerabilities in the security of a computer or network system by studying how long it takes the system to respond to different inputs.\n",
"\n",
"[Example](https://ropesec.com/articles/timing-attacks/)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"52.2 ns ± 4.71 ns per loop (mean ± std. dev. of 200 runs, 10 loops each)\n"
]
}
],
"source": [
"%%timeit -n 10 -r 200\n",
"\"abc\" == \"abc\""
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"141 ns ± 11.9 ns per loop (mean ± std. dev. of 200 runs, 10 loops each)\n"
]
}
],
"source": [
"%%timeit -n 10 -r 200\n",
"secrets.compare_digest(\"abc\", \"abc\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Recipes and best practices](https://docs.python.org/3/library/secrets.html#recipes-and-best-practices)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "pyenv37",
"language": "python",
"name": "pyenv37"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment