未验证 提交 eba9f232 编写于 作者: B BaiJiangJie 提交者: GitHub

Merge pull request #4204 from jumpserver/dev_login_password_encrypt

feat(Login password ecrypt): 登录密码加密传输
......@@ -10,6 +10,7 @@ from users.utils import (
)
reason_password_failed = 'password_failed'
reason_password_decrypt_failed = 'password_decrypt_failed'
reason_mfa_failed = 'mfa_failed'
reason_mfa_unset = 'mfa_unset'
reason_user_not_exist = 'user_not_exist'
......@@ -19,6 +20,7 @@ reason_user_inactive = 'user_inactive'
reason_choices = {
reason_password_failed: _('Username/password check failed'),
reason_password_decrypt_failed: _('Password decrypt failed'),
reason_mfa_failed: _('MFA failed'),
reason_mfa_unset: _('MFA unset'),
reason_user_not_exist: _("Username does not exist"),
......
......@@ -10,7 +10,7 @@ class UserLoginForm(forms.Form):
username = forms.CharField(label=_('Username'), max_length=100)
password = forms.CharField(
label=_('Password'), widget=forms.PasswordInput,
max_length=128, strip=False
max_length=1024, strip=False
)
def confirm_login_allowed(self, user):
......
......@@ -7,7 +7,7 @@
{% endblock %}
{% block content %}
<form class="m-t" role="form" method="post" action="">
<form id="form" class="m-t" role="form" method="post" action="">
{% csrf_token %}
{% if form.non_field_errors %}
<div style="line-height: 17px;">
......@@ -26,7 +26,7 @@
{% endif %}
</div>
<div class="form-group">
<input type="password" class="form-control" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
<input type="password" class="form-control" id="password" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
{% if form.errors.password %}
<div class="help-block field-error">
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
......@@ -36,7 +36,7 @@
<div>
{{ form.captcha }}
</div>
<button type="submit" class="btn btn-primary block full-width m-b">{% trans 'Login' %}</button>
<button type="submit" class="btn btn-primary block full-width m-b" onclick="doLogin();return false;">{% trans 'Login' %}</button>
{% if demo_mode %}
<p class="text-muted font-bold" style="color: red">
......@@ -64,4 +64,20 @@
{% endif %}
</form>
<script type="text/javascript" src="/static/js/plugins/jsencrypt/jsencrypt.min.js"></script>
<script>
function encryptLoginPassword(password, rsaPublicKey){
var jsencrypt = new JSEncrypt(); //加密对象
jsencrypt.setPublicKey(rsaPublicKey); // 设置密钥
return jsencrypt.encrypt(password); //加密
}
function doLogin() {
//公钥加密
var rsaPublicKey = "{{ rsa_public_key }}"
var password =$('#password').val(); //明文密码
var passwordEncrypted = encryptLoginPassword(password, rsaPublicKey)
$('#password').val(passwordEncrypted); //返回给密码输入input
$('#form').submit();//post提交
}
</script>
{% endblock %}
......@@ -98,7 +98,7 @@
{% endif %}
</div>
<div class="form-group">
<input type="password" class="form-control" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
<input type="password" class="form-control" id="password" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
{% if form.errors.password %}
<div class="help-block field-error">
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
......@@ -109,7 +109,7 @@
{{ form.captcha }}
</div>
<div class="form-group" style="margin-top: 10px">
<button type="submit" class="btn btn-transparent">{% trans 'Login' %}</button>
<button type="submit" class="btn btn-transparent" onclick="doLogin();return false;">{% trans 'Login' %}</button>
</div>
<div style="text-align: center">
<a href="{% url 'authentication:forgot-password' %}">
......@@ -127,4 +127,21 @@
</div>
</body>
<script type="text/javascript" src="/static/js/plugins/jsencrypt/jsencrypt.min.js"></script>
<script>
function encryptLoginPassword(password, rsaPublicKey){
var jsencrypt = new JSEncrypt(); //加密对象
jsencrypt.setPublicKey(rsaPublicKey); // 设置密钥
return jsencrypt.encrypt(password); //加密
}
function doLogin() {
//公钥加密
var rsaPublicKey = "{{ rsa_public_key }}"
var password =$('#password').val(); //明文密码
var passwordEncrypted = encryptLoginPassword(password, rsaPublicKey)
$('#password').val(passwordEncrypted); //返回给密码输入input
$('#contact-form').submit();//post提交
}
</script>
</html>
from .utils import gen_key_pair, rsa_decrypt, rsa_encrypt
def test_rsa_encrypt_decrypt(message='test-password-$%^&*'):
""" 测试加密/解密 """
print('Need to encrypt message: {}'.format(message))
rsa_private_key, rsa_public_key = gen_key_pair()
print('RSA public key: \n{}'.format(rsa_public_key))
print('RSA private key: \n{}'.format(rsa_private_key))
message_encrypted = rsa_encrypt(message, rsa_public_key)
print('Encrypted message: {}'.format(message_encrypted))
message_decrypted = rsa_decrypt(message_encrypted, rsa_private_key)
print('Decrypted message: {}'.format(message_decrypted))
# -*- coding: utf-8 -*-
#
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from Crypto import Random
from django.contrib.auth import authenticate
from common.utils import get_logger
from . import errors
logger = get_logger(__file__)
def gen_key_pair():
""" 生成加密key
用于登录页面提交用户名/密码时,对密码进行加密(前端)/解密(后端)
"""
random_generator = Random.new().read
rsa = RSA.generate(1024, random_generator)
rsa_private_key = rsa.exportKey().decode()
rsa_public_key = rsa.publickey().exportKey().decode()
return rsa_private_key, rsa_public_key
def rsa_encrypt(message, rsa_public_key):
""" 加密登录密码 """
key = RSA.importKey(rsa_public_key)
cipher = PKCS1_v1_5.new(key)
cipher_text = base64.b64encode(cipher.encrypt(message.encode())).decode()
return cipher_text
def rsa_decrypt(cipher_text, rsa_private_key=None):
""" 解密登录密码 """
if rsa_private_key is None:
# rsa_private_key 为 None,可以能是API请求认证,不需要解密
return cipher_text
key = RSA.importKey(rsa_private_key)
cipher = PKCS1_v1_5.new(key)
message = cipher.decrypt(base64.b64decode(cipher_text.encode()), 'error').decode()
return message
def check_user_valid(**kwargs):
password = kwargs.pop('password', None)
......@@ -11,6 +49,16 @@ def check_user_valid(**kwargs):
username = kwargs.pop('username', None)
request = kwargs.get('request')
# 获取解密密钥,对密码进行解密
rsa_private_key = request.session.get('rsa_private_key')
if rsa_private_key is not None:
try:
password = rsa_decrypt(password, rsa_private_key)
except Exception as e:
logger.error(e, exc_info=True)
logger.error('Need decrypt password => {}'.format(password))
return None, errors.reason_password_decrypt_failed
user = authenticate(request, username=username,
password=password, public_key=public_key)
if not user:
......
......@@ -22,7 +22,7 @@ from common.utils import get_request_ip, get_object_or_none
from users.utils import (
redirect_user_first_login_or_index
)
from .. import forms, mixins, errors
from .. import forms, mixins, errors, utils
__all__ = [
......@@ -108,9 +108,13 @@ class UserLoginView(mixins.AuthMixin, FormView):
return self.form_class
def get_context_data(self, **kwargs):
# 生成加解密密钥对,public_key传递给前端,private_key存入session中供解密使用
rsa_private_key, rsa_public_key = utils.gen_key_pair()
self.request.session['rsa_private_key'] = rsa_private_key
context = {
'demo_mode': os.environ.get("DEMO_MODE"),
'AUTH_OPENID': settings.AUTH_OPENID,
'rsa_public_key': rsa_public_key.replace('\n', '\\n')
}
kwargs.update(context)
return super().get_context_data(**kwargs)
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-06-28 18:58+0800\n"
"POT-Creation-Date: 2020-06-30 17:24+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n"
......@@ -84,7 +84,7 @@ msgstr "数据库"
#: users/templates/users/user_group_detail.html:62
#: users/templates/users/user_group_list.html:16
#: users/templates/users/user_profile.html:138
#: xpack/plugins/change_auth_plan/models.py:77 xpack/plugins/cloud/models.py:53
#: xpack/plugins/change_auth_plan/models.py:76 xpack/plugins/cloud/models.py:53
#: xpack/plugins/cloud/models.py:139 xpack/plugins/gathered_user/models.py:26
msgid "Comment"
msgstr "备注"
......@@ -110,7 +110,7 @@ msgstr "数据库应用"
#: users/templates/users/user_asset_permission.html:40
#: users/templates/users/user_asset_permission.html:70
#: users/templates/users/user_granted_remote_app.html:36
#: xpack/plugins/change_auth_plan/models.py:283
#: xpack/plugins/change_auth_plan/models.py:282
#: xpack/plugins/cloud/models.py:269
msgid "Asset"
msgstr "资产"
......@@ -134,7 +134,7 @@ msgstr "参数"
#: assets/models/group.py:21 common/mixins/models.py:49 orgs/models.py:16
#: perms/models/base.py:54 users/models/user.py:508
#: users/serializers/group.py:35 users/templates/users/user_detail.html:97
#: xpack/plugins/change_auth_plan/models.py:81 xpack/plugins/cloud/models.py:56
#: xpack/plugins/change_auth_plan/models.py:80 xpack/plugins/cloud/models.py:56
#: xpack/plugins/cloud/models.py:145 xpack/plugins/gathered_user/models.py:30
msgid "Created by"
msgstr "创建者"
......@@ -233,7 +233,7 @@ msgstr "网域"
#: assets/models/asset.py:195 assets/models/user.py:109
#: perms/models/asset_permission.py:81
#: xpack/plugins/change_auth_plan/models.py:56
#: xpack/plugins/change_auth_plan/models.py:55
#: xpack/plugins/gathered_user/models.py:24
msgid "Nodes"
msgstr "节点"
......@@ -343,8 +343,8 @@ msgstr ""
#: users/templates/users/user_detail.html:53
#: users/templates/users/user_list.html:15
#: users/templates/users/user_profile.html:47
#: xpack/plugins/change_auth_plan/models.py:47
#: xpack/plugins/change_auth_plan/models.py:279
#: xpack/plugins/change_auth_plan/models.py:46
#: xpack/plugins/change_auth_plan/models.py:278
msgid "Username"
msgstr "用户名"
......@@ -359,21 +359,21 @@ msgstr "用户名"
#: users/templates/users/user_profile_update.html:41
#: users/templates/users/user_pubkey_update.html:41
#: users/templates/users/user_update.html:20
#: xpack/plugins/change_auth_plan/models.py:68
#: xpack/plugins/change_auth_plan/models.py:191
#: xpack/plugins/change_auth_plan/models.py:286
#: xpack/plugins/change_auth_plan/models.py:67
#: xpack/plugins/change_auth_plan/models.py:190
#: xpack/plugins/change_auth_plan/models.py:285
msgid "Password"
msgstr "密码"
#: assets/models/base.py:235 xpack/plugins/change_auth_plan/models.py:72
#: xpack/plugins/change_auth_plan/models.py:198
#: xpack/plugins/change_auth_plan/models.py:293
#: assets/models/base.py:235 xpack/plugins/change_auth_plan/models.py:71
#: xpack/plugins/change_auth_plan/models.py:197
#: xpack/plugins/change_auth_plan/models.py:292
msgid "SSH private key"
msgstr "SSH密钥"
#: assets/models/base.py:236 xpack/plugins/change_auth_plan/models.py:75
#: xpack/plugins/change_auth_plan/models.py:194
#: xpack/plugins/change_auth_plan/models.py:289
#: assets/models/base.py:236 xpack/plugins/change_auth_plan/models.py:74
#: xpack/plugins/change_auth_plan/models.py:193
#: xpack/plugins/change_auth_plan/models.py:288
msgid "SSH public key"
msgstr "SSH公钥"
......@@ -603,7 +603,7 @@ msgid "Username same with user"
msgstr "用户名与用户相同"
#: assets/models/user.py:110 templates/_nav.html:39
#: xpack/plugins/change_auth_plan/models.py:52
#: xpack/plugins/change_auth_plan/models.py:51
msgid "Assets"
msgstr "资产管理"
......@@ -914,8 +914,8 @@ msgid "Success"
msgstr "成功"
#: audits/models.py:43 ops/models/command.py:28 perms/models/base.py:52
#: terminal/models.py:199 xpack/plugins/change_auth_plan/models.py:177
#: xpack/plugins/change_auth_plan/models.py:308
#: terminal/models.py:199 xpack/plugins/change_auth_plan/models.py:176
#: xpack/plugins/change_auth_plan/models.py:307
#: xpack/plugins/gathered_user/models.py:76
msgid "Date start"
msgstr "开始日期"
......@@ -999,7 +999,7 @@ msgstr "Agent"
msgid "MFA"
msgstr "多因子认证"
#: audits/models.py:105 xpack/plugins/change_auth_plan/models.py:304
#: audits/models.py:105 xpack/plugins/change_auth_plan/models.py:303
#: xpack/plugins/cloud/models.py:217
msgid "Reason"
msgstr "原因"
......@@ -1085,39 +1085,43 @@ msgstr ""
msgid "Invalid token or cache refreshed."
msgstr ""
#: authentication/errors.py:21
#: authentication/errors.py:22
msgid "Username/password check failed"
msgstr "用户名/密码 校验失败"
#: authentication/errors.py:22
#: authentication/errors.py:23
msgid "Password decrypt failed"
msgstr "密码解密失败"
#: authentication/errors.py:24
msgid "MFA failed"
msgstr "多因子认证失败"
#: authentication/errors.py:23
#: authentication/errors.py:25
msgid "MFA unset"
msgstr "多因子认证没有设定"
#: authentication/errors.py:24
#: authentication/errors.py:26
msgid "Username does not exist"
msgstr "用户名不存在"
#: authentication/errors.py:25
#: authentication/errors.py:27
msgid "Password expired"
msgstr "密码已过期"
#: authentication/errors.py:26
#: authentication/errors.py:28
msgid "Disabled or expired"
msgstr "禁用或失效"
#: authentication/errors.py:27
#: authentication/errors.py:29
msgid "This account is inactive."
msgstr "此账户已禁用"
#: authentication/errors.py:37
#: authentication/errors.py:39
msgid "No session found, check your cookie"
msgstr "会话已变更,刷新页面"
#: authentication/errors.py:39
#: authentication/errors.py:41
#, python-brace-format
msgid ""
"The username or password you entered is incorrect, please enter it again. "
......@@ -1127,34 +1131,34 @@ msgstr ""
"您输入的用户名或密码不正确,请重新输入。 您还可以尝试 {times_try} 次(账号将"
"被临时 锁定 {block_time} 分钟)"
#: authentication/errors.py:45
#: authentication/errors.py:47
msgid ""
"The account has been locked (please contact admin to unlock it or try again "
"after {} minutes)"
msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)"
#: authentication/errors.py:48 users/views/profile/otp.py:63
#: authentication/errors.py:50 users/views/profile/otp.py:63
#: users/views/profile/otp.py:102 users/views/profile/otp.py:121
msgid "MFA code invalid, or ntp sync server time"
msgstr "MFA验证码不正确,或者服务器端时间不对"
#: authentication/errors.py:50
#: authentication/errors.py:52
msgid "MFA required"
msgstr "需要多因子认证"
#: authentication/errors.py:51
#: authentication/errors.py:53
msgid "MFA not set, please set it first"
msgstr "多因子认证没有设置,请先完成设置"
#: authentication/errors.py:52
#: authentication/errors.py:54
msgid "Login confirm required"
msgstr "需要登录复核"
#: authentication/errors.py:53
#: authentication/errors.py:55
msgid "Wait login confirm ticket for accept"
msgstr "等待登录复核处理"
#: authentication/errors.py:54
#: authentication/errors.py:56
msgid "Login confirm ticket was {}"
msgstr "登录复核 {}"
......@@ -1334,7 +1338,7 @@ msgstr "欢迎回来,请输入用户名和密码登录"
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
#: authentication/views/login.py:168
#: authentication/views/login.py:172
msgid ""
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
" Don't close this page"
......@@ -1342,15 +1346,15 @@ msgstr ""
"等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n"
" 不要关闭本页面"
#: authentication/views/login.py:173
#: authentication/views/login.py:177
msgid "No ticket found"
msgstr "没有发现工单"
#: authentication/views/login.py:205
#: authentication/views/login.py:209
msgid "Logout success"
msgstr "退出登录成功"
#: authentication/views/login.py:206
#: authentication/views/login.py:210
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
......@@ -1552,8 +1556,8 @@ msgstr "开始时间"
msgid "End time"
msgstr "完成时间"
#: ops/models/adhoc.py:242 xpack/plugins/change_auth_plan/models.py:180
#: xpack/plugins/change_auth_plan/models.py:311
#: ops/models/adhoc.py:242 xpack/plugins/change_auth_plan/models.py:179
#: xpack/plugins/change_auth_plan/models.py:310
#: xpack/plugins/gathered_user/models.py:79
msgid "Time"
msgstr "时间"
......@@ -2666,7 +2670,7 @@ msgid "Set password"
msgstr "设置密码"
#: users/forms/user.py:132 users/serializers/user.py:38
#: xpack/plugins/change_auth_plan/models.py:61
#: xpack/plugins/change_auth_plan/models.py:60
#: xpack/plugins/change_auth_plan/serializers.py:30
msgid "Password strategy"
msgstr "密码策略"
......@@ -3542,65 +3546,65 @@ msgid "Token invalid or expired"
msgstr "Token错误或失效"
#: xpack/plugins/change_auth_plan/meta.py:9
#: xpack/plugins/change_auth_plan/models.py:89
#: xpack/plugins/change_auth_plan/models.py:184
#: xpack/plugins/change_auth_plan/models.py:88
#: xpack/plugins/change_auth_plan/models.py:183
msgid "Change auth plan"
msgstr "改密计划"
#: xpack/plugins/change_auth_plan/models.py:41
#: xpack/plugins/change_auth_plan/models.py:40
msgid "Custom password"
msgstr "自定义密码"
#: xpack/plugins/change_auth_plan/models.py:42
#: xpack/plugins/change_auth_plan/models.py:41
msgid "All assets use the same random password"
msgstr "所有资产使用相同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:43
#: xpack/plugins/change_auth_plan/models.py:42
msgid "All assets use different random password"
msgstr "所有资产使用不同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:65
#: xpack/plugins/change_auth_plan/models.py:64
msgid "Password rules"
msgstr "密码规则"
#: xpack/plugins/change_auth_plan/models.py:188
#: xpack/plugins/change_auth_plan/models.py:187
msgid "Change auth plan snapshot"
msgstr "改密计划快照"
#: xpack/plugins/change_auth_plan/models.py:203
#: xpack/plugins/change_auth_plan/models.py:297
#: xpack/plugins/change_auth_plan/models.py:202
#: xpack/plugins/change_auth_plan/models.py:296
msgid "Change auth plan execution"
msgstr "改密计划执行"
#: xpack/plugins/change_auth_plan/models.py:270
#: xpack/plugins/change_auth_plan/models.py:269
msgid "Ready"
msgstr ""
#: xpack/plugins/change_auth_plan/models.py:271
#: xpack/plugins/change_auth_plan/models.py:270
msgid "Preflight check"
msgstr ""
#: xpack/plugins/change_auth_plan/models.py:272
#: xpack/plugins/change_auth_plan/models.py:271
msgid "Change auth"
msgstr ""
#: xpack/plugins/change_auth_plan/models.py:273
#: xpack/plugins/change_auth_plan/models.py:272
msgid "Verify auth"
msgstr ""
#: xpack/plugins/change_auth_plan/models.py:274
#: xpack/plugins/change_auth_plan/models.py:273
msgid "Keep auth"
msgstr ""
#: xpack/plugins/change_auth_plan/models.py:275
#: xpack/plugins/change_auth_plan/models.py:274
msgid "Finished"
msgstr "结束"
#: xpack/plugins/change_auth_plan/models.py:301
#: xpack/plugins/change_auth_plan/models.py:300
msgid "Step"
msgstr "步骤"
#: xpack/plugins/change_auth_plan/models.py:318
#: xpack/plugins/change_auth_plan/models.py:317
msgid "Change auth plan task"
msgstr "改密计划任务"
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册