무작위 및 안전한 난수 보안

머리말:

        최근 코드 감사에서는 코드에 의해 난수가 생성되는 것을 자주 봅니다. 스캐너는 난수를 생성하려면 seurerandom을 사용해야 한다고 프롬프트합니다. 난 무작위가 얼마나 안전하지 않은지 깊이 연구하지 않았습니다. 오늘 공부할 시간이 있습니다. 기록하겠습니다. 아래 분석.

원칙:

무작위의:

        먼저 난수 생성을 위한 소스 코드를 살펴보세요.

         시드가 설정되지 않은 경우 기본 무작위로 생성된 시드 코드는 seedUniquifier() ^ System.nanoTime()입니다. 시드 생성은 두 값에 따라 달라집니다. 하나는 AtomicLong 유형의 seedUniquifier이고 다른 하나는 시간입니다. 여기서는 SeedUniquifier의 초기화된 값은 고정되어 있고 생성된 결과는 예측 가능하며 후자에 실제로 영향을 미치는 것은 System.nanoTime()입니다. 여기서 System.nanoTime()은 밀리초 16자리 타임스탬프를 생성하므로 Random의 시드는 주로 기반입니다. 정시에 생성되며 예측할 수 있습니다.

        시드로 난수를 생성하는 방법을 살펴보겠습니다.nextInt를 실행하면 내부 코드는 다음과 같습니다.

         소스코드에 따르면 승수, 가수, 마스크 3가지 값이 모두 하드코딩되어 있는 것을 볼 수 있는데, 난수 생성에 실제로 영향을 미치는 시드가 바로 시드입니다. 원래 시드를 알고 있으므로 (oldseed * multiplier + addend) & 마스크를 사용하여 최신 시드와 해당 유형의 임의 값을 알 수 있습니다.

보안랜덤:

        SecureRandom이 생성되는 방법을 이해하기 전에 실제 난수와 의사 난수가 무엇인지 이해해야 합니다.

        진난수는 일반적으로 하드웨어 난수 생성기에서 파생되며, 매번 생성되는 난수는 진난수이지만 물리적 요인으로 인해 생성 시간이 느립니다.

        의사 난수는 일반적으로 특정 생성 알고리즘에서 나오며, 매번 생성되는 난수는 무작위이지만 여전히 생성 알고리즘의 규칙을 따르므로 생성 속도가 더 빠르다는 장점이 있습니다.

        실제 난수를 생성하는 모듈을 TRNG(진정한 난수 생성기)라고 하고 의사 난수를 생성하는 모듈을 PRNG(의사 난수 생성기)(결정론적 난수 생성기, DRBG라고도 함)라고 합니다. 의사 난수에 사용되는 시드를 엔트로피 소스(엔트로피 풀)라고 합니다.

의사 난수 생성 알고리즘, NIST SP 800-90A 사양에서는 의사 난수 생성을 위한 세 가지 알고리즘을 설명합니다.

  • Hash_DRBG: 의사 난수 생성을 위한 기본 알고리즘으로 단방향 해시 알고리즘을 사용합니다.
  • HMAC_DRBG: 난수 생성을 위한 기본 알고리즘으로 메시지 인증 코드 알고리즘을 사용합니다.
  • CRT_DRBG: 난수 생성을 위한 기본 알고리즘으로 블록 암호 알고리즘의 카운터 모드를 사용합니다.

        SecureRandom, NativePRNG, DRBG 및 SHA1PRNG에서 사용하는 세 가지 유형의 알고리즘이 있으며 코드는 다음과 같습니다.

        기본값은 DRBG를 사용하는 것이며, NativePRNG는 지정된 경우에만 사용할 수 있습니다.

  • /dev/random 장치는 엔트로피 풀의 총 노이즈보다 적은 임의 바이트를 반환합니다. /dev/random은 무작위 공개 키 또는 일회용 패드를 생성합니다. 엔트로피 풀이 비어 있으면 /dev/random에 대한 읽기 작업이 차단되고 영원히 대기하므로 JVM이 강제로 대기하게 되어 온라인 문제가 발생하므로 여기서 주의하세요.
  • /dev/urandom("잠금 해제된", 비차단 난수 생성기)은 NativePRNG로, 엔트로피 풀의 데이터를 재사용하여 의사 난수 데이터를 생성합니다. 이는 /dev/urandom에 대한 읽기 작업이 차단되지 않지만 해당 출력의 엔트로피가 /dev/random의 엔트로피보다 작을 수 있음을 의미합니다.

        서버에서 키보드와 마우스 입력 및 디스크 활동이 부족하면 필요한 임의성 또는 엔트로피가 생성될 수 있으므로 /dev/random을 사용하면 시스템 문제가 발생할 가능성이 높으므로 -Djava.security.egd=file:/dev/를 사용할 수 있습니다. . /urandom (파일 이름에 u가 여러 개 있음) /dev/urandom 파일을 강제로 사용하도록 합니다.

      또한 Windows에는 "file:/dev/random" 또는 "file:/dev/urandom"이 없으므로 MS CryptoAPI를 사용하여 난수를 생성합니다.

        Windows에서 SecureRandom이 시드를 생성하는 방법을 살펴보겠습니다. nextInt를 호출할 때 첫 번째 항목은 Random의 nextInt이지만 시드 생성은 SecureRandom에 입력됩니다.

        그런 다음 엔트로피가 존재하는지 확인합니다. 첫 번째 호출은 시드를 생성한 다음 인스턴스화를 True로 설정하고 후속 nextint는 새 시드를 생성하지 않습니다.

         Seed 생성은 MessageDigest를 통해 정보 다이제스트 알고리즘의 기능을 제공하며, 고정 길이의 해시 값을 출력하며, 시스템 구성 및 시스템 임시 파일에서 얻은 파일 이름 등의 정보를 해시용 문자열로 임의의 값으로 사용합니다. 각 기계에서 생성된 가치가 시스템과 강한 상관관계를 갖도록 보장하여 시드가 고유하고 추측할 수 없도록 보장합니다.

        SecureRandom이 시드를 생성하는 방식은 주로 trmp 파일에 포함된 시스템 정보, 파일명 등의 정보를 엔트로피로 이용하여 시드를 생성하는 것을 알 수 있는데, 추측할 수 없는 시스템 정보와 임시 파일의 통제할 수 없는 내용으로 인해 공격자는 이를 수행할 수 없다. 종자의 생성을 예측하므로 당연히 공격할 방법이 없습니다. 그러나 사용자가 setSeed를 사용하여 직접 시드를 설정하는 경우 위에서 언급한 시드 생성 과정에 들어가지 않고 사용자가 설정한 시드를 사용하여 난수를 생성하므로 보안이 크게 저하됩니다.

시험:

스프링 환경을 구축합니다.

        사실 테스트를 위해 스프링 환경을 구축할 필요는 없지만, 가능한 실제 상황을 시뮬레이션하는 것이 더 몰입감이 좋습니다. 빌드 과정도 기록합니다. 저는 springmvc 메소드를 사용하여 빌드하는데 springboot가 빌드하기 더 쉬운데 실수로 springmvc를 사용하여 빌드했는데 이 부분은 빌드 과정을 기록하는 것이므로 잊어버리지 않도록 건너뛰어도 됩니다. 직접요.

        먼저 Maven을 사용하여 프로젝트를 생성한 다음 웹을 추가하고 파일->프로젝트 구조를 선택하고 webapp 디렉터리와 웹, xml 위치가 올바른지 확인하십시오.

        그런 다음 pom.xml에 필요한 타사 jar 패키지를 추가하고 여기에서 <packaging>war</packaging>에 주의하여 패키징 유형을 war로 설정하세요.

<packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <!-- 作用在打包时确保servlet不会打包进去 -->
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2.1-b03</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>

    </dependencies>

    <!-- 插件 -->
    <build>
        <plugins>
            <!-- 编码和编译和JDK版本 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <!-- 根据自己电脑中的jdk版本选择maven的版本,如果不匹配可能报错 -->
                <version>3.8.1</version>
                <configuration>
                    <!-- 自己电脑中的jdk版本 -->
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <!--tomcat插件-->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/</path>
                    <!-- 可自定义访问端口 -->
                    <port>1234</port>
                </configuration>
            </plugin>
        </plugins>
    </build>

    그런 다음 스프링 구성 파일을 생성하고, 먼저 applicationContext.xml을 생성하고, 스프링 컨테이너에 대한 구성 및 제약 조건을 추가합니다.

        생성이 성공하면 xmlns:context 및 xsi:schemaLocation이 새로 추가된 다음 내용을 작성합니다. 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- Spring配置文件:除了控制器之外的bean对象都在这被扫描 -->
    <context:component-scan base-package="org.example.dao"/>
</beans>

        그런 다음 springmvc 구성 파일을 만들고 다음을 작성합니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        ">
    <!-- SpringMVC配置文件:控制器的bean对象在这被扫描 -->
    <context:component-scan base-package="org.example.controller"/>
    <!--    启动mvc的注解-->
    <mvc:annotation-driven/>
    <!--    配置视图解析器的配置-->                                  
    <!--    调用视图解析器的方法:InternalResourceViewResolver-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前缀 默认访问路径是webapp根路径下的,如果webapp下还有其他文件夹就写:/webapp/文件夹名-->
        <property name="prefix" value="/"/>
        <!-- 后缀 如果是index.html文件,就写html -->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

        그런 다음 web.xml을 수정하고, applicationContext.xml을 로드하는 리스너를 생성하고, springmvc의 구성 파일 springmvc.xml을 읽기 위한 서블릿을 로드합니다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- Spring配置-->

    <!-- 1、让监听器知道spring的配置文件的位置-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <!-- spring配置文件的文件名 -->
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <!-- 2.创建监听器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- springmvc的 核心\前端\中央 控制器-->

    <!-- servlet的封装-->

    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- servlet读取springmvc的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- springmvc配置文件的文件名 -->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 在容器创建servlet对象的优先级.数字越小越先创建 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!-- 设置访问路径后必须加.do才能进行访问 -->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

</web-app>

         그런 다음 이 템플릿에 따라 생성할 수 있는 코드에 폴더를 생성하면 수정되지 않습니다.

controller文件夹 :一般是放web控制器的服务类(就是根据前端发来的请求进行数据的处理,然后返回给前端)
dao文件夹 :一般是放数据库的操作类(数据库相关操作数据访问等)
pojo文件夹 :一般是放实体类(用来数据库映射对象)
service文件夹 :一般是做相应的业务逻辑处理的服务类

       그런 다음 코드를 작성해야 하며 먼저 서비스에서 ChangePasswordImpl을 생성해야 합니다. 코드는 다음과 같습니다.

package org.example.service;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;

public class ChangePasswordImpl{
    public void hello(){
        System.out.println("TeamService---------hello");
    }
    
    public static String SecurRandomscramble(SecureRandom secureRandom, String inputstring){
        char a[] = inputstring.toCharArray();
        for(int i=0; i<a.length; i++){
            int j = secureRandom.nextInt(a.length);
            char temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        return new String(a);
    }
    public String seurerandom(String seed) {
        try {
            String outstr = "name:admin";
            String username = "admin";
            //Random random = new Random();
            SecureRandom secRandom = new SecureRandom();
            if(!seed.equals("")) {
                Long aLong = Long.valueOf(seed).longValue();
                secRandom.setSeed(aLong);
            }
            MessageDigest md = MessageDigest.getInstance("MD5");// 生成一个MD5加密计算摘要
            md.update(username.getBytes());// 计算md5函数
            String hashedPwd = new BigInteger(1, md.digest()).toString(16);// 16是表示转换为16进制数
            outstr = outstr + "</h1><h1>MD5hash:" +hashedPwd;
            String string1 = SecurRandomscramble(secRandom, hashedPwd);
            outstr = outstr + "</h1><h1>encode1:" + string1;
            String string2 = SecurRandomscramble(secRandom, string1);
            outstr = outstr + "</h1><h1>encode2:" + string2;
            return outstr;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }
    
    public static String Randomscramble(Random random, String inputstring){
        char a[] = inputstring.toCharArray();
        for(int i=0; i<a.length; i++){
            int j = random.nextInt(a.length);
            char temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        return new String(a);
    }
    
    public String random(String seed) {
        try {
            String outstr = "name:admin";
            String username = "admin";
            Random random = new Random();
            if(!seed.equals("")) {
                Long aLong = Long.valueOf(seed).longValue();
                random.setSeed(aLong);
            }
            MessageDigest md = MessageDigest.getInstance("MD5");// 生成一个MD5加密计算摘要
            md.update(username.getBytes());// 计算md5函数
            String hashedPwd = new BigInteger(1, md.digest()).toString(16);// 16是表示转换为16进制数
            outstr = outstr + "</h1><h1>MD5hash:" +hashedPwd;
            String string1 = Randomscramble(random, hashedPwd);
            outstr = outstr + "</h1><h1>encode1:" + string1;
            String string2 = Randomscramble(random, string1);
            outstr = outstr + "</h1><h1>encode2:" + string2;
            return outstr;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

}

        그런 다음 컨트롤러에서 ChangePasswordServlet을 생성합니다. 코드는 다음과 같습니다.

package org.example.controller;

import org.example.service.ChangePasswordImpl;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServlet;

@Controller
public class ChangePasswordServlet extends HttpServlet {
    ChangePasswordImpl changePassword = new ChangePasswordImpl();
    @RequestMapping("/index.do")//设置请求路的径
    public ModelAndView hello(){
        changePassword.hello();
        ModelAndView mv = new ModelAndView();
        mv.addObject("team", "teamindex----hello");//相当于request.setAttribute("team", "teamindex----hello")
        //经过InternalResourceViewResolver对象处理后前缀加上后缀就变为了:    /文件夹/index.jsp
        mv.setViewName("index");//未来要经过Springmvc的视图解析器处理,转换成物理资源路径。/相当于request.getRequestDispatcher("index.jsp").forward();
        return mv;
    }


    //设置请求路的径  规定请求的方式是post
    @RequestMapping(value = "/seurerandom.do",method = RequestMethod.GET)//请求方式设定后,只能用post的提交方式
    public ModelAndView seurerandom(@RequestParam("key") String key, @RequestParam("seed") String seed){
        String checkkey = "False";
        String back = changePassword.seurerandom(seed);
        ModelAndView mv = new ModelAndView();

        mv.addObject("backinfor", back);
        mv.addObject("mykey", key);
        if(back.contains(key)){
            checkkey = "Success";
        }
        mv.addObject("checkkey", checkkey);
        //经过InternalResourceViewResolver对象处理后前缀加上后缀就变为了:    /jsp/team/update.jsp
        mv.setViewName("/jsp/CheckSecureRandom");//要经过Springmvc的视图解析器处理,转换成物理资源路径。
        return mv;
    }

    @RequestMapping(value = "/random.do",method = RequestMethod.GET)//请求方式设定后,只能用post的提交方式
    public ModelAndView random(@RequestParam("key") String key, @RequestParam("seed") String seed){
        String checkkey = "False";
        String back = changePassword.random(seed);
        ModelAndView mv = new ModelAndView();
        mv.addObject("backinfor", back);
        mv.addObject("mykey", key);
        if(back.contains(key)){
            checkkey = "Success";
        }
        mv.addObject("checkkey", checkkey);
        //经过InternalResourceViewResolver对象处理后前缀加上后缀就变为了:    /jsp/team/update.jsp
        mv.setViewName("/jsp/CheckRandom");//要经过Springmvc的视图解析器处理,转换成物理资源路径。
        return mv;
    }
}

        그런 다음 해당 인터페이스를 작성해야 합니다. 여기에서는 jsp 코드가 사용됩니다. webapp에서 Index.jsp가 생성되고 코드가 추가됩니다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>index</title>
</head>
<body>
<h1>${team}</h1>
<form action="/seurerandom.do" method="get">
    <button type="submit">seurerandom提交方式</button>
</form>
<form action="/random.do" method="post">
    <button type="submit">random提交方式</button>
</form>
</body>
</html>

        그런 다음 webapp 아래에 jsp 폴더를 만들고 그 안에 동일한 내용으로 CheckRandom.jsp 및 CheckSecureRandom.jsp라는 두 개의 jsp 파일을 만듭니다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Check Random</title>
    <style type="text/css">
      html, body {
        height: 100%;
        overflow: auto;
      }
      body {
        padding: 0;
        margin: 0;
      }
    </style>
  </head>
  <body>
    <div style="width:100%;text-align:center"></div>
    <center>
      <br>
      <br>
      <br>
      <h1>${backinfor}</h1>
      <h1>user post key:${mykey}</h1>
      <h1>check key:${checkkey}</h1>
      <br>
</center>
</body>
</html>

        그런 다음 Tomcat을 통해 프로젝트를 실행하여 다음에 액세스할 수 있습니다.

시드를 사용하여 난수 생성을 테스트합니다.

        시드가 설정되지 않은 경우 Random은 시간에 따라 시드를 생성하고 SecureRandom은 시스템 파일 및 시스템 정보를 통해 시드를 생성합니다. 그런 다음 시드가 설정되면 SecureRandom은 안전합니다.

package org.example;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;


public class Main {


    public static String scramble(Random random, String inputstring){
        char a[] = inputstring.toCharArray();
        for(int i=0; i<a.length; i++){
            int j = random.nextInt(a.length);
            char temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        return new String(a);
    }

    public static String seurescramble(SecureRandom random, String inputstring){
        char a[] = inputstring.toCharArray();
        for(int i=0; i<a.length; i++){
            int j = random.nextInt(a.length);
            char temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        return new String(a);
    }

    public static String seurerandom(String seed) {
        try {
            String username = "admin";
            SecureRandom secRandom = new SecureRandom();

            if(!seed.equals("")) {
                Long aLong = Long.valueOf(seed).longValue();
                secRandom.setSeed(aLong);
            }
            MessageDigest md = MessageDigest.getInstance("MD5");// 生成一个MD5加密计算摘要
            md.update(username.getBytes());// 计算md5函数
            String hashedPwd = new BigInteger(1, md.digest()).toString(16);// 16是表示转换为16进制数
            System.out.print("admin MD5:" + hashedPwd+ "\n");
            String string1 = seurescramble(secRandom, hashedPwd);
            System.out.print(string1+ "\n");
            String string2 = seurescramble(secRandom, string1);
            System.out.print(string2+ "\n");

            return string2;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String random(String seed) {
        try {
            String username = "admin";
            Random random = new Random();
            if(!seed.equals("")) {
                Long aLong = Long.valueOf(seed).longValue();
                random.setSeed(aLong);
            }
            MessageDigest md = MessageDigest.getInstance("MD5");// 生成一个MD5加密计算摘要
            md.update(username.getBytes());// 计算md5函数
            String hashedPwd = new BigInteger(1, md.digest()).toString(16);// 16是表示转换为16进制数
            System.out.print("admin MD5:" + hashedPwd + "\n");
            String string1 = scramble(random, hashedPwd);
            System.out.print(string1+ "\n");
            String string2 = scramble(random, string1);
            System.out.print(string2+ "\n");
            return string2;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        for (int i =0; i<5; i++) {
            seurerandom("11");
        }
        System.out.print("========================\n");
        for (int i =0; i<5; i++) {
            random("11");
        }
    }

}

        시드를 11로 설정했으며 로컬 실행 후 결과는 다음과 같습니다.

         Random을 사용하여 생성된 값은 모두 동일한 값이라고 볼 수 있지만 securerandom을 사용하여 생성된 값은 매번 다르지만 이 Random 값이 정말 Random인지 아닌지를 알아보기 위해 코드를 다른 시스템에 넣어두었습니다. 실행:

javac -encoding utf-8 Main.java

자바 메인

         비교 결과, 시드를 설정하면 다양한 환경에서 생성되는 값도 예측할 수 있으며, securerandom을 사용하더라도 개발자가 키 생성 등을 위해 인위적으로 시드를 설정하면 크래킹이 가능하다는 것을 알 수 있습니다.

        여기서는 위에 구축된 웹을 사용하여 비밀번호 재설정 기능을 시뮬레이션합니다. 비밀번호를 잊어버린 경우 비밀번호 재설정 링크가 있습니다. 링크는 사용자 이름 md5를 사용하고 이를 임의의 숫자로 두 번 암호화하여 얻습니다. 코드는 다음과 같습니다. 인터넷에 있는 코드이므로 더 좋습니다. 대표님, 예측할 수 있는지 테스트해 보겠습니다.

        11을 시드로 사용하면 첫 번째 암호화의 결과가 9183780aa702a33744252cf524a91aef라는 것을 알 수 있으며, 이전에 생성한 것과 비교하면 세 번째는 9183780aa702a33744252cf524a91aef이므로 더 많은 값을 생성합니다.

        이런 식으로 우리는 다음 값이 1caa4138793af7a449058232520fe27a이고 최종 생성된 값이 8204af23e738a15fac59a32407412a79라고 예측합니다. 다음 테스트는 다음과 같습니다.

         예측이 성공한 것을 확인할 수 있는데, Random을 사용하여 생성된 값이 매번 동일하면 테스트가 수행되지 않습니다.

요약하다:

        따라서 Random과 securerandom의 소스코드를 분석한 결과, 기본적으로 Random은 시드가 설정되지 않은 경우에는 안전하지 않다는 것을 알 수 있다. 공격자는 로컬 시간만 시드로 사용하거나 난수 발파 시드에 따라 시스템의 난수 보안이 깨질 수 있지만 시간 비용은 조금 더 높지만 이론적으로는 깨질 수 있지만, 경우에 따라 securerandom의 경우, 시드가 설정되지 않으면 시스템 정보 및 파일명을 기반으로 시드를 생성하기 어려우며, 생성된 난수는 예측적으로 더 안전합니다.

        그러나 개발자가 수동으로 시드를 설정하면 보안이 크게 저하되며, 시드를 획득한 이상 공격자는 시드를 기반으로 난수 목록을 생성한 후 다음 난수가 무엇인지 예측할 수 있으며, 이로 인해 난수 인증이 중단됩니다.

        따라서 난수를 생성하려면 securerandom을 사용하고 수동으로 시드를 설정하지 마십시오.linux에서 securerandom을 사용하면 엔트로피 소스로 /dev/random을 사용하므로 스레드 대기가 발생하지만 서버에서는 키보드 작업 및 기타 작업이 더 적게 생성됩니다. , 이는 컨텐츠가 너무 적기 때문에 발생할 수 있습니다. 대기를 방지하기 위해 수동으로 /dev/urandom으로 설정할 수 있습니다.

Supongo que te gusta

Origin blog.csdn.net/GalaxySpaceX/article/details/131961017
Recomendado
Clasificación