Code updated to use crypt
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
venv/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
31
app.py
31
app.py
@@ -1,16 +1,17 @@
|
|||||||
from flask import Flask, request, jsonify, render_template, abort
|
from flask import Flask, request, jsonify, render_template, abort
|
||||||
from passlib.hash import sha512_crypt, sha256_crypt, md5_crypt
|
import secrets, crypt, os
|
||||||
import secrets, os
|
|
||||||
|
|
||||||
app = Flask(__name__, static_folder='static', template_folder='templates')
|
app = Flask(__name__, static_folder='static', template_folder='templates')
|
||||||
|
|
||||||
SALT_CHARS = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
SALT_CHARS = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
MIN_LEN = 16
|
MIN_LEN = 16
|
||||||
|
MIN_SALT_LEN = 8
|
||||||
MAX_SALT_LEN = 16
|
MAX_SALT_LEN = 16
|
||||||
|
|
||||||
ALG_MAP = {
|
|
||||||
'sha512': sha512_crypt,
|
ALG_PREFIX = {
|
||||||
'sha256': sha256_crypt
|
'sha512': '$6$',
|
||||||
|
'sha256': '$5$',
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
@@ -19,9 +20,11 @@ def index():
|
|||||||
|
|
||||||
@app.route('/gensalt')
|
@app.route('/gensalt')
|
||||||
def gensalt():
|
def gensalt():
|
||||||
salt = ''.join(secrets.choice(SALT_CHARS) for _ in range(MAX_SALT_LEN))
|
length = max(MIN_SALT_LEN, min(MAX_SALT_LEN, int(request.args.get('length', MIN_SALT_LEN))))
|
||||||
|
salt = ''.join(secrets.choice(SALT_CHARS) for _ in range(length))
|
||||||
return jsonify({'salt': salt})
|
return jsonify({'salt': salt})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/hash', methods=['POST'])
|
@app.route('/hash', methods=['POST'])
|
||||||
def do_hash():
|
def do_hash():
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
@@ -29,23 +32,25 @@ def do_hash():
|
|||||||
salt = data.get('salt', '')
|
salt = data.get('salt', '')
|
||||||
algorithm = data.get('algorithm', 'sha512')
|
algorithm = data.get('algorithm', 'sha512')
|
||||||
|
|
||||||
|
|
||||||
if not isinstance(password, str) or not isinstance(salt, str):
|
if not isinstance(password, str) or not isinstance(salt, str):
|
||||||
abort(400, 'Invalid input')
|
abort(400, 'Invalid input')
|
||||||
if len(password) < MIN_LEN:
|
if len(password) < MIN_LEN:
|
||||||
abort(400, f'Password must be at least {MIN_LEN} characters')
|
abort(400, f'Password must be at least {MIN_LEN} characters')
|
||||||
if len(salt) < MIN_LEN or len(salt) > MAX_SALT_LEN:
|
if len(salt) < MIN_SALT_LEN or len(salt) > MAX_SALT_LEN:
|
||||||
abort(400, f'Salt must be between {MIN_LEN} and {MAX_SALT_LEN} characters')
|
abort(400, f'Salt must be between {MIN_SALT_LEN} and {MAX_SALT_LEN} characters')
|
||||||
|
|
||||||
hash_class = ALG_MAP.get(algorithm)
|
prefix = ALG_PREFIX.get(algorithm)
|
||||||
if hash_class is None:
|
|
||||||
|
if prefix is None:
|
||||||
abort(400, 'Unsupported algorithm')
|
abort(400, 'Unsupported algorithm')
|
||||||
|
|
||||||
# truncate salt to MAX_SALT_LEN just in case
|
|
||||||
salt_to_use = salt[:MAX_SALT_LEN]
|
|
||||||
|
|
||||||
hashed = hash_class.using(salt=salt_to_use).hash(password)
|
full_salt = f"{prefix}{salt}"
|
||||||
|
hashed = crypt.crypt(password, full_salt)
|
||||||
return jsonify({'hash': hashed})
|
return jsonify({'hash': hashed})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
host = os.environ.get('HOST', '127.0.0.1')
|
host = os.environ.get('HOST', '127.0.0.1')
|
||||||
port = int(os.environ.get('PORT', 4444))
|
port = int(os.environ.get('PORT', 4444))
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ button {
|
|||||||
color: white
|
color: white
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px
|
gap: 8px
|
||||||
@@ -6,25 +6,32 @@ const hashBtn = document.getElementById('hashBtn');
|
|||||||
const result = document.getElementById('result');
|
const result = document.getElementById('result');
|
||||||
const clearBtn = document.getElementById('clearBtn');
|
const clearBtn = document.getElementById('clearBtn');
|
||||||
|
|
||||||
const MIN_LEN = 16;
|
const MIN_PASS_LEN = 16;
|
||||||
|
const MIN_SALT_LEN = 8;
|
||||||
|
const MAX_SALT_LEN = 16;
|
||||||
|
|
||||||
gensaltBtn.addEventListener('click', async () => {
|
gensaltBtn.addEventListener('click', async () => {
|
||||||
const len = Math.max(MIN_LEN, MIN_LEN);
|
const len = MAX_SALT_LEN; // can be adjusted or user-defined
|
||||||
const res = await fetch('/gensalt?length=' + len);
|
const res = await fetch('/gensalt?length=' + len);
|
||||||
if (!res.ok) { alert('Could not generate salt'); return; }
|
if (!res.ok) { alert('Could not generate salt'); return; }
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
salt.value = data.salt;
|
salt.value = data.salt;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
hashBtn.addEventListener('click', async () => {
|
hashBtn.addEventListener('click', async () => {
|
||||||
const pass = password.value || '';
|
const pass = password.value || '';
|
||||||
const s = salt.value || '';
|
const s = salt.value || '';
|
||||||
const alg = algorithm.value;
|
const alg = algorithm.value;
|
||||||
|
|
||||||
if (pass.length < MIN_LEN) { alert('Password must be at least ' + MIN_LEN + ' characters'); return; }
|
if (pass.length < MIN_PASS_LEN) {
|
||||||
|
alert('Password must be at least ' + MIN_PASS_LEN + ' characters');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (s.length < MIN_LEN) { alert('Salt must be at least ' + MIN_LEN + ' characters'); return; }
|
if (s.length < MIN_SALT_LEN || s.length > MAX_SALT_LEN) {
|
||||||
|
alert('Salt must be between ' + MIN_SALT_LEN + ' and ' + MAX_SALT_LEN + ' characters');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const payload = { password: pass, salt: s, algorithm: alg };
|
const payload = { password: pass, salt: s, algorithm: alg };
|
||||||
const res = await fetch('/hash', {
|
const res = await fetch('/hash', {
|
||||||
@@ -41,9 +48,9 @@ hashBtn.addEventListener('click', async () => {
|
|||||||
result.value = data.hash;
|
result.value = data.hash;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
clearBtn.addEventListener('click', () => {
|
clearBtn.addEventListener('click', () => {
|
||||||
password.value = '';
|
password.value = '';
|
||||||
salt.value = '';
|
salt.value = '';
|
||||||
result.value = '';
|
result.value = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<input id="password" type="password" placeholder="Enter password" minlength="16">
|
<input id="password" type="password" placeholder="Enter password" minlength="16">
|
||||||
|
|
||||||
|
|
||||||
<label for="salt">Salt (maximum 16 characters)</label>
|
<label for="salt">Salt (between 8 and 16 characters)</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input id="salt" type="text" placeholder="Enter salt or generate one">
|
<input id="salt" type="text" placeholder="Enter salt or generate one">
|
||||||
<button id="gensaltBtn" type="button" class="small">Generate salt</button>
|
<button id="gensaltBtn" type="button" class="small">Generate salt</button>
|
||||||
Reference in New Issue
Block a user