对Nacos登录参数password进行sha256加密

为什么要加密?

很多人可能都在使用nacos,但是在使用nacos的时候有没有留意到自己重要的密码都放到nacos配置中心管理了,觉得Jar包中没有数据库和各种中间件的密码,但是我今天告诉你一件细思极恐的事情,那就是你登录nacos的时候使用F12打开浏览器的开发者工具,你会发现登录接口/nacos/v1/auth/users/loginpassword居然是明文的????

是不是很不安全??虽然nacos大部分都是内网使用,但是安全团队那群人整天会说不能弱口令,安全第一。隔三差五给你来一波扫描,今天扫出来/nacos/v1/cs/ops/derby?sql=有安全漏洞,明天就说抓包发现你这个网址登录密码被我看到了。给你一两天时间要求整改

简直苦不堪言,茶饭不思,但是你看到了小明的这篇文章,瞬间活了过来,一顿CV,半天搞定~

如何加密?

首先要思考一个问题,造成password明文的地方是在F12网络看到的,那我们要修改的肯定是从前端代码入手,需要在输入密码后,将密码进行加密,然后后端拿到加密后的秘钥串构造token。

那么下一步当然是赶紧把源码下载下来,用idea打开,然后搜索/nacos/v1/auth/users/login定位代码。

package.json

#在console_fe目录执行
cd nacos-1.3.0\console\src\main\resources\static\console-fe
#添加crypto库
yarn add crypto
#安装前端依赖库
yarn

Login.jsx

import React from 'react';
import { Card, Form, Input, Message, ConfigProvider, Field } from '@alifd/next';
import { withRouter } from 'react-router-dom';

import './index.scss';
import Header from '../../layouts/Header';
import PropTypes from 'prop-types';
import { login } from '../../reducers/base';

// import CryptoJS from 'crypto-js';


//添加crypto库
const crypto = require('crypto');
function sha256Encrypt(password) {
  // 创建SHA256哈希对象
  const hash = crypto.createHash('sha256');
  // 将要哈希的数据作为流传递给哈希对象
  hash.update(password);
  // 获取哈希结果
  const hashValue = hash.digest('hex');
  return hashValue.toString();
}

const FormItem = Form.Item;

@withRouter
@ConfigProvider.config
class Login extends React.Component {
  static displayName = 'Login';

  static propTypes = {
    locale: PropTypes.object,
    history: PropTypes.object,
  };

  constructor(props) {
    super(props);
    this.field = new Field(this);
  }

  componentDidMount() {
    if (localStorage.getItem('token')) {
      const [baseUrl] = location.href.split('#');
      location.href = `${baseUrl}#/`;
    }
  }

  handleSubmit = () => {
    const { locale = {} } = this.props;
    this.field.validate((errors, values) => {
      if (errors) {
        return;
      }
      //对密码进行SHA-256加密
      values.password = sha256Encrypt(values.password);
      login(values)
        .then(res => {
          localStorage.setItem('token', JSON.stringify(res));
          this.props.history.push('/');
        })
        .catch(() => {
          Message.error({
            content: locale.invalidUsernameOrPassword,
          });
        });
    });
  };

  onKeyDown = event => {
    // 'keypress' event misbehaves on mobile so we track 'Enter' key via 'keydown' event
    if (event.key === 'Enter') {
      event.preventDefault();
      event.stopPropagation();
      this.handleSubmit();
    }
  };

  render() {
    const { locale = {} } = this.props;

    return (
      <div className="home-page">
        <Header />
        <section
          className="top-section"
          style={{
            background: 'url(img/black_dot.png) repeat',
            backgroundSize: '14px 14px',
          }}
        >
          <div className="vertical-middle product-area">
            <img className="product-logo" src="img/nacos.png" />
            <p className="product-desc">
              an easy-to-use dynamic service discovery, configuration and service management
              platform for building cloud native applications
            </p>
          </div>
          <div className="animation animation1" />
          <div className="animation animation2" />
          <div className="animation animation3" />
          <div className="animation animation4" />
          <div className="animation animation5" />
          <Card className="login-panel" contentHeight="auto">
            <div className="login-header">{locale.login}</div>
            <Form className="login-form" field={this.field}>
              <FormItem>
                <Input
                  {...this.field.init('username', {
                    rules: [
                      {
                        required: true,
                        message: locale.usernameRequired,
                      },
                    ],
                  })}
                  placeholder={locale.pleaseInputUsername}
                  onKeyDown={this.onKeyDown}
                />
              </FormItem>
              <FormItem>
                <Input
                  htmlType="password"
                  placeholder={locale.pleaseInputPassword}
                  {...this.field.init('password', {
                    rules: [
                      {
                        required: true,
                        message: locale.passwordRequired,
                      },
                    ],
                  })}
                  onKeyDown={this.onKeyDown}
                />
              </FormItem>
              <FormItem label=" ">
                <Form.Submit onClick={this.handleSubmit}>{locale.submit}</Form.Submit>
              </FormItem>
            </Form>
          </Card>
        </section>
      </div>
    );
  }
}

export default Login;

authority.js

/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *      http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Message } from '@alifd/next';
import request from '../utils/request';
import { UPDATE_USER, SIGN_IN, USER_LIST, ROLE_LIST, PERMISSIONS_LIST } from '../constants';

const crypto = require('crypto');

function sha256Encrypt(password) {
  // 创建SHA256哈希对象
  const hash = crypto.createHash('sha256');
  // 将要哈希的数据作为流传递给哈希对象
  hash.update(password);
  // 获取哈希结果
  const hashValue = hash.digest('hex');
  return hashValue.toString();
}

const initialState = {
  users: {
    totalCount: 0,
    pageNumber: 1,
    pagesAvailable: 1,
    pageItems: [],
  },
  roles: {
    totalCount: 0,
    pageNumber: 1,
    pagesAvailable: 1,
    pageItems: [],
  },
  permissions: {
    totalCount: 0,
    pageNumber: 1,
    pagesAvailable: 1,
    pageItems: [],
  },
};

const successMsg = res => {
  if (res.code === 200) {
    Message.success(res.message);
  }
  return res;
};

/**
 * 用户列表
 * @param {*} params
 */
const getUsers = params => dispatch =>
  request.get('v1/auth/users', { params }).then(data => dispatch({ type: USER_LIST, data }));

/**
 * 创建用户
 * @param {*} param0
 */
const createUser = ([username, password]) => {
  password = sha256Encrypt(password);
  return request.post('v1/auth/users', { username, password }).then(res => successMsg(res));
};


/**
 * 删除用户
 * @param {*} username
 */
const deleteUser = username =>
  request.delete('v1/auth/users', { params: { username } }).then(res => successMsg(res));

/**
 * 重置密码
 * @param {*} param0
 */
const passwordReset = ([username, newPassword]) => {
  newPassword = sha256Encrypt(newPassword);
  return request.put('v1/auth/users', { username, newPassword }).then(res => successMsg(res));
};


/**
 * 角色列表
 * @param {*} params
 */

const getRoles = params => dispatch =>
  request.get('v1/auth/roles', { params }).then(data => dispatch({ type: ROLE_LIST, data }));

/**
 * 创建角色
 * @param {*} param0
 */
const createRole = ([role, username]) =>
  request.post('v1/auth/roles', { role, username }).then(res => successMsg(res));

/**
 * 删除角色
 * @param {*} param0
 */
const deleteRole = role =>
  request.delete('v1/auth/roles', { params: role }).then(res => successMsg(res));

/**
 * 权限列表
 * @param {*} params
 */
const getPermissions = params => dispatch =>
  request
    .get('v1/auth/permissions', { params })
    .then(data => dispatch({ type: PERMISSIONS_LIST, data }));

/**
 * 给角色添加权限
 * @param {*} param0
 */
const createPermission = ([role, resource, action]) =>
  request.post('v1/auth/permissions', { role, resource, action }).then(res => successMsg(res));

/**
 * 删除权限
 * @param {*} param0
 */
const deletePermission = permission =>
  request.delete('v1/auth/permissions', { params: permission }).then(res => successMsg(res));

export default (state = initialState, action) => {
  switch (action.type) {
    case USER_LIST:
      return { ...state, users: { ...action.data } };
    case ROLE_LIST:
      return { ...state, roles: { ...action.data } };
    case PERMISSIONS_LIST:
      return { ...state, permissions: { ...action.data } };
    default:
      return state;
  }
};

export {
  getUsers,
  createUser,
  deleteUser,
  passwordReset,
  getRoles,
  createRole,
  deleteRole,
  getPermissions,
  createPermission,
  deletePermission,
};

如何安装部署

重新构建前端资源

#重新打包静态资源
yarn build

nacos打包

#将dist文件夹下静态资源拷到static目录
cp /nacos-1.3.0/console/src/main/resources/static/console-fe/dist/* /nacos-1.3.0/console/src/main/resources/static/console-fe/

#重新打包nacos
cd /nacos-1.3.0
mvn clean install -U -DskipTests=true

替换target中的nacos-server.jar

#拷贝targer下jar包覆盖原先的包
cp /nacos-1.3.0/distribution/target/nacos-server-1.3.0/nacos/target/nacos-server.jar /服务部署位置/nacos/target/

替换微服务中的密码

spring:
  cloud:
    nacos:
      username: nacos
      #更换密码
      password: 569bf0af7a7562f31bbe4795656b6bdf307f7752163abc139157e3a3033b43ff

注意事项

线上是明文密码要提前更换成sha256加密后的密码,例如密码是nacos,提前通过sha256("nacos")得到569bf0af7a7562f31bbe4795656b6bdf307f7752163abc139157e3a3033b43ff,通过修改密码功能进行修改,然后再覆盖nacos-server.jar重启nacos完成升级。

Tips:如果你忘记了这一步,可以通过curl请求derby接口或直接操作数据库动态修改nacos密码

#password可以通过new BCryptPasswordEncoder().encode(sha256("nacos"))生成
#这里password是'nacos','$2a$10$mI4gqx8HCVxc3hozBgAM2uroyzZD7HSmxfQx9aCO4sUH0jjF7mce.'示例
#derby数据库可以通过下面curl进行修改
curl -X GET http://127.0.0.1:8848/nacos/v1/cs/ops/derby?sql=update users set password='$2a$10$mI4gqx8HCVxc3hozBgAM2uroyzZD7HSmxfQx9aCO4sUH0jjF7mce.' where username='nacos'

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值