主页

网络安全

2021-08-01
计算机网络, 信息安全

安全通信需要具有下列的特性

  • 机密性:仅有发送方和希望的接收方能够理解传输报文的内容。

  • 报文完整性:确保其通信内容在传输的过程中未被改变。

  • 端点鉴别:发送方和接收方都能证实通信过程所涉及的另一方。

  • 运行安全性:确保网络安全。

密码学 #

报文的最初形式称为明文

使用加密算法加密明文,生成的加密报文称为密文,加密算法往往是已知的。

密钥KA是一串数字或者字符,加密算法以密钥和明文报文m为输入,生成的密文为输出。用KA(m)表示。

解密算法使用密钥KB和密文作为输入,通过计算KB(KA(m))得到m

对称密钥密码体制 #

  1. 凯撒密码
    • 将明文报文中的每个字母用字母表中该字母后第k个字母进行替换,允许回绕。
    • 缺点:密钥值只有25个
  2. 单码代替密码
    • 使用字母表中的一个字母替换另一个字母。需要给定一个替换表,每个字母都有唯一一个的替换字母。
    • 缺点:由于是定向替换,当对密文进行统计分析,并且常见的两三个字母的组合(in、on、the)往往是一起出现,而且如果入侵者知道该密文具有某些可能的东西,破解该密文就不那么困难了。
  3. 多码代替密码
    • 使用多个单码来替换明文的字母,即从多个字母中选一个代替明文中的一个字母,如何选怎呢?需要指定一个次序,比如第一次选择多个字母的第一个,第二次选择多个字母的第二个,第三次选择多个字母的第二个(122),然后按照这个顺序循环多次,直到完成加密。

块密码:要加密的报文被处理为k比特的块。例如。如果k=64,字报文被划分为多个64比特的块,每块被独立加密。

密码块链接:使用块密码时,如果有多个明文块可能是相同的,将会产生相同的密文,解决方式是:发送方为第i个块生成一个随机的k比特数,通过将该数与第i个明文块异或,再将结果加密。该方法有一个问题,就是传输的时候需要两倍的带宽,块密码使用了一种称为密码块链接(Cipher Block Chaining,CBC)的技术,基本思想是只传输一个随机值,剩余的让发送方和接收方计算。流程如下:

  1. 发送方生成一个随机的k比特串,称为初始向量c(0),发送方以明文方式发送。

  2. 对于第一个块,发送方计算m(1)⊕c(0),然后加密得到c(1)=Ks(m(1)⊕c(0))。发送方向接收方发送加密块c(1)

  3. 对于第i个块,发送方根据c(i)=Ks(m(1)⊕c(i-1))向接收方发送第i个密文块。

公开密钥加密 #

世界上任何人都都可以得到公钥(public key)KB+

只有主人知道的私钥(private key)KB-

假如A要和B通信,A首先要取得B的公钥,然后用这个公钥和一个众所周知的加密算法加密他要传给B的报文m,即KB+(m),B接到A的加密报文后,用其私钥和一个众所周知的解密算法解密报文,即计算KB-(KB+(m)),这样就得到了最初的明文m。此外还有KB-(KB+(m)) = KB+(KB-(m))=m。

  1. RSA
    • 生成RSA的公钥和私钥
      1. 选择两个大素数p和q,p和q越大,破解RSA越困难,执行加密和解密所用的时间也越长。推荐p和q的乘积为1024比特的数量级
      2. 计算n=pq,z=(p-1)(q-1)
      3. 选择一个小于n的数e,使得e和z没有公因数,e用来加密
      4. 求得一个数d使得ed-1可以被z整除,d将用来解密(ed mod z = 1)
      5. 外界可用的公钥KB+是一对数(n,e),私钥KB-是一对数(n,d)
    • 加密和解密过程
      1. A给B发送一个由整数m表示的比特组合,m<n。加密后的值c=me mod n,将密文c发送给B
      2. B通过计算获得m,m=cd mod n
  2. 会话密钥:RSA的指数运算极其耗时,在实际应用中,RSA通常与对称密钥结合使用。首先,A选择一个用于加密数据本身的密钥,这个密钥有时候称为会话密钥Ks。A通过公钥将Ks传输给B,B通过私钥解密后,就获取了Ks,然后使用Ks来传输数据。

报文完整性和数字签名 #

密码散列函数 #

密码散列函数的性质:找到任意两个不同的报文x和y使得H(x)=H(y),在计算上是不可能的。

算法:MD5(128位)、SHA-1(160位)

为了执行报文完整性,A和B需要共享秘密s,这个共享的秘密是个比特串,被称为鉴别密钥。使用这个共享秘密,报文完整性能够执行如下:

  1. A生成报文m,用s级联m生成m+s,并计算散列H(m+s)。H(m+s)被称为报文鉴别码(Message Authentication Code,MAC)。
  2. A将MAC附加到报文m上,生成扩展报文(m,H(m+s)),将该扩展报文发送给B。
  3. B收到扩展报文(m,h),由于知道s,计算出报文鉴别码H(m+s),如果其等于h,则证明一切正常。

数字签名 #

数字签名是B先使用散列函数对报文生成散列,使用私钥对散列进行加密(数字签名)。

A获取到了B的明文报文和已经数字签名的报文摘要,先使用B的公钥解密数字签名,得到一个散列结果,再使用散列函数获取报文的散列,如果这两个散列匹配,则可以确信报文的完整性和发送方。

公钥认证 #

证实一个公钥属于某个特定的实体。

将公钥与特定的实体绑定通常是由认证中心(Certification Authority,CA)完成的。CA证实了一个实体的真实身份;一旦CA验证了某个实体的身份,这个CA会生成一个将其身份和实体的公钥绑定起来的证书。

使TCP连接安全:SSL #

SSL:Secure Socket Layer,安全套接口协议

TLS:Transport Layer Security,TLS 运输层安全性,是SSL版本3的一个稍加修改的版本

equals

2021-04-26
java

Object #

  • Object的equals方法
    public boolean equals(Object obj) {
        return (this == obj);
    }
  • Object的toString方法
	public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

String #

  • String的equals方法
    public boolean equals(Object anObject) {
        //如果指向的是同一个对象就返回true
        if (this == anObject) {
            return true;
        }
        //如果该对象是String的实例
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            //value存放的是String本身的字符串
            int n = value.length;
            //如果两个字符串的长度相等
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                //按下标比较两个字符串的字符
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
  • String的hashCode方法
	private int hash; // Default to 0	
	//...
	//当通过new String("xxx")方式创建的字符串,原hash就被赋值给了现在的hash
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
	//当hash为0并且字符串长度不为0时,现在的hash是通过循环计算出来的
	//由该计算可知道,当字符串一样,它的hashcode就一样
	public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

maven-dependency-scope

2021-04-23
java

共6个作用域 #

  1. compile

    • 默认作用域,在项目的编译、运行、测试中都有效,依赖传递
  2. provided

    • 在项目编译、测试中有效,运行中无效;依赖不传递,适用于一些web容器提供了运行时的依赖的情况。
  3. runtime

    • 只在项目运行、测试中有效,依赖传递
  4. test

    • 表示程序正常使用时不需要该依赖,只在测试编译和执行中有效。依赖不传递
  5. system

    • 和provided很像,但是必须明确指定jar路径,它不在仓库中去寻找
  6. import

执行ssh命令

2021-04-19
java

  • 如何使用ssh连接linux执行shell命令?
package space.xiaoxiang;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 通过ssh连接到linux主机,并执行一些shell命令,需要导入JSch这个包
 */
public class App {

    private static final String host = "localhost";
    private static final int port = 22;
    private static final String username = "xiaoxiang";
    private static final String password = "asdf";

    //连接到linux主机
    public Session connect() throws  JSchException {
        JSch jsch = new JSch();
        Session session = jsch.getSession(username, host, port);
        session.setPassword(password);
        session.setConfig("StrictHostKeyChecking", "no");
        //开启会话
        session.connect();
        return session;
    }

    //执行shell命令
    public void execCommand(Session session, String command) throws JSchException, IOException {
        ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
        InputStream in = channelExec.getInputStream();
        channelExec.setCommand(command);
        BufferedReader reader = new BufferedReader(new InputStreamReader(channelExec.getInputStream()));
        channelExec.connect();
        String line;
        while((line = reader.readLine()) != null){
            System.out.println(line);
        }
        channelExec.disconnect();
    }

    public static void main(String[] args) throws IOException, JSchException {
        App app = new App();
        Session session = app.connect();
        app.execCommand(session, "ls -l");
        System.out.println("----------");
        app.execCommand(session, "df -h");
        session.disconnect();
    }
}

mybatis入门

2021-04-02
java

  1. 写mybatis配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--数据库配置文件路径-->
    <!--<properties resource="config.properties"/>-->
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射文件位置-->
    <mappers>
        <mapper resource="space/xiaoxiang/dao/XxxxMapper.xml"/>
        <!--<package name="space.xiaoxiang.dao"/> 使用package接口的包名必须与xml包名一样-->
    </mappers>
</configuration>
  1. 数据库配置文件config.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/xxxxx?serverTimezone=GMT%2B8&characterEncoding=utf-8
username=xxxx
password=xxxxxxxx
  1. 写dao层接口和xml文件

    1. 接口
    package space.xiaoxiang.dao;
    
    import java.util.List;
    import java.util.Map;
    
    public interface XxxxMapper {
        public List<Map<String, Object>> selectAll();
    }
    
    1. xml文件
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="space.xiaoxiang.dao.XxxxtMapper">
        <select id="selectAll" resultType="map">
            SELECT * FROM xxxx
        </select>
    </mapper>
    
  2. 运行

@Test
public void test() throws IOException {
    //数据库配置文件
    String databaseConfigFileName = "config.properties";
    Properties databaseConfig = new Properties();
    databaseConfig.load(Resources.getResourceAsStream(databaseConfigFileName));
    //mybatis配置文件
    String mybatisConfigFileName = "mybatis-config.xml";
    InputStream mybatisConfig = Resources.getResourceAsStream(mybatisConfigFileName);
    //一个数据库对应一个SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(mybatisConfig,databaseConfig);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
        XxxxMapper xxxxMapper = sqlSession.getMapper(XxxxMapper.class);
        xxxxMapper.selectAll().forEach((map) -> {
            xxxxMapper.forEach((k, v) -> {
                System.out.println(k + " : " + v);
            });
        });
    }
}

zookeeper起步

2021-03-27
java

  • zkCli.sh -server localhost:2181:连接到zookeeper

  • 复制模式下的配置小结(以3台linux举例)

    1. 为每一台linux安装上zookeeper
    2. 修改他们的配置文件zoo.cfg(3份可以一样)
    3. 在dataDir路径下创建myid文件,里面写一个数字,与server.x的x对应
    4. 每一台都启动zookeeper
#zookeeper使用的基本时间单位
tickTime=2000
#存储内存数据库快照的位置
dataDir=/var/lib/zookeeper
#对client端提供服务
clientPort=2181
#超时
initLimit=5
#限制服务器与领导者之间过时的距离
syncLimit=2
#server.X格式列出了组成zookeeper服务的服务器。服务器启动时,它通过在数据目录中查找文件myid*来知道它是哪台服务器。
#2888集群内机器通信使用,3888选举leader使用
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888
  • zkServer.sh start:启动zookeeper
  • zkServer.sh status:查看当前状态
  • zkCli.sh -server 127.0.0.1:2181:连接到zookeeper
    • ls /
    • create /zk_test my_data:创建一个新的znode,并将字符串my_data与该节点关联
    • get /zk_test:验证数据是否与znode关联
    • set /zk_test junk:来更改与zk_test相关的数据
    • delete /zk_test

Znode #

  • 组成
    • data:Znode存储的数据信息
    • stat:包含Znode的各种元数据,如事务ID、版本号、时间戳、大小等
    • child:当前节点的子节点引用
    • acl:access control list访问控制表,表示哪些人或哪些ip可以访问本节点
  • Znode是用来存储少量的状态和配置信息,每个节点的数据最大不能超过1MB

clickhouse类型及函数

2021-03-25
数据库

类型 #

Decimal(P,S), Decimal32(S), Decimal64(S), Decimal128(S) #

  • 有符号的定点数,可在加减乘中保持精度,除法的最低有效数字会被丢弃(不舍入)

参数 #

  • P表示精度,有效范围是[1:38],决定可以有多少个十进制的数字
  • S表示规模,有效范围是[0:P],决定数字的小数部分中包含的小数位数
  • P[1:9]对应Decimal32(S)
  • P[10:18]对应Decimal64(S)
  • P[19:38]对应Decimal128(S)
  • 精度

函数 #

parseDateTimeBestEffort(time_string[, time_zone]) #

  • 把其他类型的时间日期转换为DateTime数据类型。
  • 支持的格式:timestamp YYYYMMDDHHMMSS DD/MM/YYYY hh:mm:ss YYYY-MM-DD hh:mm:ss

SHOW CREATE TABLE #

  • 返回创建该表的语句

类型转换 #

  • clickhouse是强类型,他不会在类型之间进行隐式转换。

toTypeName(x) #

  • 返回该参数的类型

CORS

2021-03-24
http

  • 跨源资源共享(Cross-Origin Resource Sharing),CORS是一种基于HTTP头的机制,该机制通过允许服务器标识除了它自己以外的其他origin(源是协议、主机名和端口号的组合),这样其他origin就可以加载这些资源。

  • XMLHttpRequest和Fetch API遵循同源策略。

  • Access-Control-Allow-Origin: *:允许所有origin访问该资源。

同源策略 #

  • 同源是指协议相同、主机名相同、端口相同。
  • 非同源会限制Cookie和Ajax。

访问控制场景 #

  • 浏览器将CORS请求分为两类,简单请求和非简单请求。

简单请求 #

  • 如果请求的HTTP方法是GET、HEAD、POST,并且HTTP头部除了被用户代理自动设置的首部字段外,只有Accept-Language、Content-Language、Content-Type ;并且Content-Type 的值仅限于下列三者之一: text/plain、multipart/form-data、application/x-www-form-urlencoded。那么Web浏览器发出的是简单请求。发出简单请求时,该请求将像一般请求一样发送到服务器。
  • 简单请求不会触发CORS 预检请求。

预检请求 #

  • 预检请求会先使用OPTION方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。预检请求的使用可以避免跨域请求对服务器的用户数据产生未预期的影响。当预检不通过,实际的请求将不会发送。

HTTP响应首部 #

Access-Control-Allow-Origin #

Access-Control-Allow-Origin: <origin> | *
  • origin参数的值指定了允许访问该资源的外域URL,对不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。
  • 如果服务器指定了具体的域名而非"*",那么响应首部中的Vary字段的值必须包含Origin。这将告诉客户端,服务器对不同的源站返回不同的内容。

Accsess-Control-Expose-Headers #

  • 在跨源访问时,XMLHttpRequest对象的getResponseHeaders方法只能拿到Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,需要服务器设置响应头。
  • Accsess-Control-Expose-Headers头让服务器把允许浏览器访问的头放入白名单。
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

Access-Control-Max-Age #

  • 指定了预检的请求的结果能被缓存多久,单位是秒。
Access-Control-Max-Age: 3600

Access-Control-Allow-Credentials #

  • Access-Control-Allow-Credentials 头指定了当浏览器的 credentials 设置为 true 时是否允许浏览器读取 response 的内容。当用在 预检测请求的响应中时,它指定了实际的请求是否可以使用 credentials。简单 GET 请求不会被预检。

Access-Control-Allow-Methods #

  • Access-Control-Allow-Methods 首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。
Access-Control-Allow-Methods: <method>[, <method>]*

Access-Control-Allow-Headers #

  • Access-Control-Allow-Headers 首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段。

参考链接: 跨资源共享(CORS)

参考链接: 跨域资源共享 - 阮一峰的网络日志 (ruanyifeng.com)