Code updated to use crypt

This commit is contained in:
Eri
2025-10-12 03:06:45 +02:00
parent f5aabf7f2a
commit 5b88979921
5 changed files with 47 additions and 28 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
venv/
__pycache__/
*.pyc

31
app.py
View File

@@ -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))

View File

@@ -36,6 +36,10 @@ button {
color: white color: white
} }
button:hover {
cursor: pointer;
}
.row { .row {
display: flex; display: flex;
gap: 8px gap: 8px
@@ -58,4 +62,4 @@ button {
display: flex; display: flex;
gap: 8px; gap: 8px;
margin-top: 12px margin-top: 12px
} }

View File

@@ -6,44 +6,51 @@ 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');
if (s.length < MIN_LEN) { alert('Salt must be at least ' + MIN_LEN + ' characters'); return; } 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', {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload)
}); });
if (!res.ok) { if (!res.ok) {
const txt = await res.text(); const txt = await res.text();
alert('Error: ' + txt); alert('Error: ' + txt);
return; return;
} }
const data = await res.json(); const data = await res.json();
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 = '';
}); });

View File

@@ -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>
@@ -44,4 +44,4 @@
<script src="/static/js/main.js"></script> <script src="/static/js/main.js"></script>
</body> </body>
</html> </html>