【Python】擬似乱数を生成するrandomと機密を扱うために安全な乱数を生成するsecrets（備忘録）

自分用メモ

基本的に乱数は**random**を使えばいいが、セキュリティ目的に使用することは非推奨らしい。
セキュリティや暗号学的な用途については**secrets**を使用することが推奨されている。

# random --- 擬似乱数を生成する
よく使う関数を抜粋

## float型の乱数を取得する関数（randomの基本の関数）

### random.random()

```python
import random

print(random.random())
print(random.random())
print(random.random())
```
```
0.0016279893463542194
0.19053946851887782
0.5239379354847451
```


## 整数用の関数

### random.randrange(stop)
stopに指定した値を上限（stop–1）としてランダムな整数を返す。

```python
import random

print(random.randrange(10))
print(random.randrange(10))
print(random.randrange(10))
print(random.randrange(10))
print(random.randrange(10))

print(random.randrange(100))
print(random.randrange(100))
print(random.randrange(100))
print(random.randrange(100))
print(random.randrange(100))
```
```
1
0
0
5
8

97
8
74
63
91
```

### random.randrange(start, stop[, step])
range(start, stop, step) の要素からランダムに選ばれた要素を返す。
stopに指定した値を上限（stop–1）としてランダムな整数を返す。
※stepは増分

```python
import random

print(random.randrange(50, 100))
print(random.randrange(50, 100))
print(random.randrange(50, 100))

print(random.randrange(50, 100, 2))
print(random.randrange(50, 100, 2))
print(random.randrange(50, 100, 2))
```

後半3つはstepを2にしているので偶数のみとなっている。
print(random.randrange(50, 100, 2))

```
92
82
89

84
76
66
```

### random.randint(a, b)
a <= N <= b になるランダムな整数 N を返す。randrange(a, b+1) のエイリアス。

```python
import random

print(random.randint(100, 999))
print(random.randint(100, 999))
print(random.randint(100, 999))
```
```
434
639
501
```

## シーケンス用の関数

**random.choice(seq)**
**random.choices(population, weights=None, *, cum_weights=None, k=1)**
配列からランダムの要素を抽出する関数。

**random.shuffle(x[, random])**
配列をランダムにシャッフルする関数。

これらは個人的にあまり使わないので割愛。

## ランダム文字列の生成

```python
import random, string

print(''.join([random.choice(string.ascii_letters + string.digits) for i in range(5)]))
print(''.join([random.choice(string.ascii_letters + string.digits) for i in range(10)]))
print(''.join([random.choice(string.ascii_letters + string.digits) for i in range(30)]))
```
```
9SKZw
uabBQlzlhp
FXZG4phDeg1ArS4PdtEo8hqRuBs0fP
```

# secrets --- 機密を扱うために安全な乱数を生成する
バージョン 3.6 で追加

>secrets モジュールを使って、パスワードやアカウント認証、セキュリティトークンなどの機密を扱うのに適した、暗号学的に強い乱数を生成することができます。

とのこと。

**secrets.choice(sequence)**
空でないシーケンスから要素をランダムに選択して返す。

**secrets.randbelow(n)**
[0, n) のランダムな整数を返す。

**secrets.randbits(k)**
ランダムなkビットの整数を返す。

といったrandom関数と同じような使い方も可能。


## トークンの生成

### secrets.token_bytes([nbytes=None])
nbytesバイトを含むバイト文字列を返す
nbytesがNoneの場合妥当なデフォルト値が使われる。

```python
import secrets

print(secrets.token_bytes(10))
print(secrets.token_bytes(20))
print(secrets.token_bytes(30))
```
```
b'\xae}`\xf8\xec\xf0\x9f\x05u\xf3'
b'\x00n\x85\x9c\x13\x8e,\x8b\xb6\xe5\xe8\x9dm\xa3\xc3 \t\xb8r\xf0'
b'\xd056\x9b\x9aG\x96\xce\xd0\xb3\xc0x~\xe3\xd3;M\xd1D\x88\xed\xe9\xcb|\xde\xb3\xba\x06\xd6\x85'
```

### secrets.token_hex([nbytes=None])
16進数のランダムなテキスト文字列を返す。

ワンタイムパスワードに使えそう。

```python
import secrets

print(secrets.token_hex(10))
print(secrets.token_hex(20))
print(secrets.token_hex(30))
```
```
14f016cb4b8c4c782683
d4ad3044ac2eedcf5d79f403976290c88e9bc748
eb1c3bf515ee675f36870ead703790a41907255b0dfa82839f365a68a0b2
```

### secrets.token_urlsafe([nbytes=None])
nbytesのランダムなバイトを持つ安全なURLテキスト文字列を返す。
テキストはBase64でエンコードされていて、平均的に各バイトは約 1.3文字になる。

```python
import secrets

print(secrets.token_urlsafe(10))
print(secrets.token_urlsafe(20))
print(secrets.token_urlsafe(30))
```
```
uwBgrQKdKelA3A
uT6jLp9sTSkbzlQUqIYbh5DlHRg
Rv6aSEJHAq4V2eIL_ocSp9xK33wGNLhHbc_zyUk1
```

# secretsを使って安全なパスワードを生成する

```python
import secrets, string

def pass_gen(size=12):

   # 含める文字
   chars = string.ascii_uppercase + string.ascii_lowercase + string.digits

   # 記号を含める場合は別途指定する
   chars += '_-?%&$#()'

   return ''.join(secrets.choice(chars) for x in range(size))

print(pass_gen(10))
print(pass_gen(20))
print(pass_gen(30))
print(pass_gen(50))
```
```
BRS_MHZfAB
6XOYBcN?vFHfOEWxt3Gt
?PrDBVY21gujsJ$bn8uDddT4ybs$Rd
%vVFR1q(e%fvsIsW_aEIPUd1u2WgWYtq?hRIbtAiN5Jg$?2vNx
```

# 参考リンク
[Python docs #random](https://docs.python.org/ja/3/library/random.html)
[Python docs #secrets](https://docs.python.org/ja/3/library/secrets.html)
[Pythonで複雑なパスワードを簡単に生成する](https://gammasoft.jp/blog/generate-password-by-python/)

以上