保护 SSH 的三把锁
转自:https://www.ibm.com/developerworks/cn/aix/library/au-sshlocks/index.html
简介
如果需要远程访问计算机并启用了 Secure Shell (SSH) 连接,黑客就会尝试突破您的防线并控制您的计算机,您必须接受这个事实。尽管不能保证计算机不会被 “黑客” 占领,但是一些简单的解决方案有助于保护 SSH,可以让攻击困难一些。本文讨论三种技术:
常用缩写词
- API: 应用程序编程接口
- DNS: 域名系统
- IETF: Internet 工程工作小组
- LDAP: 轻量级目录访问协议
- RFC: 请求注解
- TCP: 传输控制协议
- UDP: 用户数据报协议
- 把 SSH 的标准端口改为不常用的值并增强 SSH 配置,从而挡住最简单的攻击。
- 定义有限的用户列表,只允许这些用户登录。
- 完全隐藏允许 SSH 访问的事实,要求根据特殊的 “敲门” 序列识别有效用户。
要想应用这些技术,需要能够访问根账户。另外,可能必须安装一些包,需要配置防火墙和路由器(如果有路由器的话),打开和关闭特定的端口并把数据包转发到您的计算机。
加强保护
“隐匿产生安全” 这个概念尽人皆知而且受到大家的嘲笑,因为采用隐匿的方式,希望没人了解您的方法,这只是一厢情愿的想法。但是,在某些场景中,隐匿一点儿会有帮助。尽管简单的措施无法阻止下定决心的黑客,但是至少能够挡住那些 “脚本小子”,他们的脚本往往水平很一般。
人人都知道 SSH 连接的标准端口是 22。因此,为了让计算机更安全,应该采取的第一个措施是把端口改为另一个不常用的非标准端口号,比如 22960。1024 以上的号码通常都可以使用,但是应该查阅参考资料以避免导致问题。这一修改对您的影响仅仅是必须使用下面的命令连接计算机:
1
|
ssh -p 22960 your.machine.url |
为了实现这个小措施,只需在 /etc/ssh/sshd_config 文件中做简单的修改。编辑此文件(必须作为根用户),寻找 Port 22
行,把端口号改为您选择的号码(如果这一行以镑符 [#
] 开头,表示它被注释掉了,那么应该取消注释标志)。保存文件,用 /etc/init.d/sshd restart
命令重新启动 SSH。应该在防火墙上打开您选择的端口并关闭端口 22。
但是,还可以更进一步。编辑配置文件,在其中包含 清单 1 所示的行。注意,其中一些行可能已经存在,但是可以把它们注释掉。
清单 1. 通过修改 SSH 配置文件简便地增强安全性12345Port 22960 LoginGraceTime 30 MaxAuthTries 3 Protocol 2 PermitRootLogin no
LoginGraceTime
允许一次登录花费 30 秒;如果用户花费的时间超过 30 秒,就不允许他访问,必须重新登录。MaxAuthTries
把错误尝试的次数限制为 3 次,3 次之后拒绝登录尝试。上面的 Protocol 2
行禁止使用比较弱的协议。最后一行不允许任何人作为根用户登录,这会让黑客攻击更困难。还可以使用 DenyUsers
、AllowUsers
、DenyGroups
和 AllowGroups
选项实现其他限制。这些修改不会显著增强计算机的安全性,但是只尝试强力攻击标准端口 22 的一般脚本会失败,不会造成损害。无论如何,这是向正确的方向迈出了第一步。在本文后面,我们将使用更安全的方法,不仅修改端口号,而且完全隐藏它。
谁可以进入?
对于大多数人,PAM 是一种罐装的烹调油。但是作为 Linux® 安全术语,PAM 代表可插入身份验证模块(Pluggable Authentication Modules)。这些模块提供额外的身份验证规则,保护对计算机的访问。
首先讨论一个基本问题:究竟为什么要使用 PAM?如果每个程序不得不定义自己的身份验证逻辑,就会很混乱。如何确定所有应用程序都实现了相同的测试和检查?如果需要额外的控制手段,那么怎么办?难道要重新编写所有程序吗?在计算机科学领域,有时候可以用额外的一层解决所有问题,至少在安全方面是这样。如果一个程序需要验证用户的身份,它可以调用 PAM API。这个 API 负责执行在 PAM 配置文件中指定的所有检查。这种方法还允许方便地修改身份验证规则,所有感知 PAM 的程序都会自动地应用新规则,不需要修改它们的代码。如果希望使用某种生物学检查(比如虹膜扫描器或指纹采集器),而且生产商提供了 PAM,就可以方便地设置它。在配置文件中包含模块调用,所有应用程序就可以使用这个设备了。
配置 PAM
PAM 提供四个安全领域的特性,但是应用程序不太可能同时需要所有这些方面。例如,passwd
命令只需要下面列表中的第三组:
account
处理账户限制。对于有效的用户,允许他做什么?auth
处理用户识别 — 例如,通过输入用户名和密码。password
只处理与密码相关的问题,比如设置新密码。session
处理连接管理,包括日志记录。
在 /etc/pam.d 目录中为将使用 PAM 的每个应用程序创建一个配置文件,文件名与应用程序名相同。例如,login
命令的配置文件是 /etc/pam.d/login。
必须定义将应用哪些模块,创建一个动作 “堆”。PAM 运行堆中的所有模块,根据它们的结果允许或拒绝用户的请求。还必须定义检查是否是必需的。最后,other 文件为没有特殊规则的所有应用程序提供默认规则。
optional
模块可以成功,也可以失败;PAM 根据模块是否最终成功返回success
或failure
。required
模块必须成功。如果失败,PAM 返回failure
,但是会在运行堆中的其他模块之后返回。requisite
模块也必须成功。但是,如果失败,PAM 立即返回failure
,不再运行其他模块。sufficient
模块在成功时导致 PAM 立即返回success
,不再运行其他模块。
配置文件的结构很简单。可以包含注释,注释以散列符 (#
) 开头;通过在换行处加上反斜杠 (\
),可以把长的行分为多行。行有三个字段:领域 (account
、auth
、password
或 session
)、控制标志(optional
、required
、requisite
或 sufficient
)、将运行的模块的路径和参数。注意,第二个字段可以更复杂;更多信息见 参考资料。另外,可以使用 include
规则以包含其他文件中的规则,比如 auth include common-account
。
特殊的 /etc/pam.d/other 文件是 “默认的” 配置文件(见 清单 2),其中的规则自动地应用于没有自己的配置文件的所有应用程序。为了确保安全,应该快速检查 /etc/pam.d 目录,把您不使用的所有配置文件改为其他名称(这样就会使用 other
配置)。如果认为确实需要某个应用程序,那么只需把配置文件改回原来的名称。默认配置通常拒绝所有请求(通过使用 pam_deny.so
模块)并警告管理员(通过 pam_warn.so
模块),让管理员解决问题。
标准的 “other” 配置文件为没有自己的配置文件的所有应用程序提供安全的默认规则(拒绝所有请求)。
清单 2. 标准的 “other” 配置文件123456account required pam_deny.so auth required pam_deny.so auth required pam_warn.so password required pam_deny.so password required pam_warn.so session required pam_deny.so
如果把 pam_deny.so
替换为 pam_unix.so
,就应用标准的身份验证方法(输入用户名和密码)。如果您不关心安全性,那么使用 pam_permit.so
,这会允许任何请求!
一些可用方法
尽管没有标准的模块列表,但是所有发行版都包含以下模块中的大多数。请检查驻留模块的 /lib/security 或 /usr/lib/security 目录。对于 64 位操作系统,用 lib64
替换 lib
。如果需要更多信息,可以尝试执行 man the.name.of.the.module
,而不要直接执行它;PAM 不是可执行的二进制代码。
pam_access
根据 /etc/security/access.conf 文件允许或拒绝访问。稍后将使用此模块决定允许哪些用户登录。pam_cracklib
和pam_pwcheck
检查新密码的强度。pam_deny
和pam_permit
是基本模块,分别拒绝或允许访问。pam_echo
向用户显示指定文件的内容。pam_lastlog
向用户显示他上一次登录的日期和时间。pam_ldap.so
让用户根据 LDAP 服务器进行身份验证,提供跨网络的集中式身份验证。pam_limits
模块允许指定系统资源限制,限制在 /etc/security/limits.conf 文件中定义。pam_listfile
提供根据一个文件的内容允许或拒绝服务的另一种方法。pam_mail
检查用户是否有未处理的邮件。pam_motd
向用户显示 “message of the day” 文件。- 如果 /etc/nologin 文件存在,
pam_nologin
阻止所有登录。 pam_rootok
允许根用户访问,不执行进一步检查。/etc/pam.d/su 中常常包含这个模块;必需的行是auth sufficient pam_rootok.so
。根用户可以作为任何用户操作,不需要提供密码。pam_succeed_if
检查账户的特定属性,比如是否是某个组的成员。pam_time
可以根据 /etc/security/time.conf 中的规则限制对服务的访问。pam_unix
(或pam_unix2
)提供基于 /etc/passwd 和 /etc/shadow 文件的传统 UNIX® 身份验证。pam_userdb
根据一个 Berkeley 数据库执行身份验证。pam_warn
在系统日志中记录信息。pam_wheel
只向 wheel 组的成员提供根访问权;必需的行是auth required pam_wheel.so
。
关于其他模块和编写自己的模块的信息,请查阅 参考资料。现在,使用 PAM 决定谁可以登录您的计算机。
用 PAM 限制访问
现在,我们来使用 PAM 限制谁可以连接您的服务器。必须编辑 /etc/pam.d/sshd 文件,让它像清单 3 这样。
清单 3. 在 sshd PAM 文件中添加 pam_access.so1234567#%PAM-1.0 account include common-account account required pam_access.so auth include common-auth auth required pam_nologin.so password include common-password session include common-session
在 sshd PAM 文件中添加 pam_access.so
,就可以轻松地定义谁可以使用 SSH 连接您的计算机。pam_access.so
模块实现基于 /etc/security/access.conf 文件的安全控制,见清单 4。
清单 4. 通过使用 pam_access.so,定义谁可以或不可以使用 SSH1234+ : ALL : 192.168.1. + : jack : ALL + : jill : ALL - : ALL : ALL
第一行允许任何用户 (ALL
) 从内部网络登录。后两行允许用户 jack
和 jill
从任何地方访问服务器。最后一行拒绝其他任何用户从其他任何地方访问。允许多个用户访问的另一种方法是使用 pam_listfile.so
,这需要创建一个允许访问的用户列表(例如 /etc/ssh_users)。在 /etc/pam.d/sshd 文件中添加以下行:
1
2
|
auth required pam_listfile.so item=user sense=allow file=/etc/ssh_users onerr=fail |
这还没有完。必须修改 /etc/ssh/sshd_config 文件,让它使用 PAM。在此文件中添加 UsePAM yes
行,重新启动 sshd
守护进程,这样就行了!
究竟是否有门?
即使应用了前两节中的方法,无论您怎么预防,黑客仍然会尝试穿越您系统中任何开放的门户。改变 SSH 端口号对于经验丰富的黑客只能造成小小的麻烦。限制允许访问的用户会有帮助,但前提是没有用户落入黑客或社会工程攻击的圈套而泄露密码。无论如何,只要您的系统中有门,就会吸引黑客。
增强计算机安全性的最后一种方案是最激进的:关闭打开的端口,这会让任何攻击都无法攻破您的计算机。只向能够提供 “秘密敲门暗号” 的用户开放所需的端口,让用户能够输入密码并访问计算机。
这种技术称为端口敲门,适用于需要访问不向公众开放的服务器的用户。服务器可以关闭所有端口,直到用户提供一个秘密的敲门序列 (序列很容易实现,而且需要的资源不多)。
打开秘密端口之后,应用常用的安全机制(比如密码或证书)。只需在防火墙级上提供一个额外的安全层,需要秘密端口的所有服务就会正常工作。
这种方法的要点在于关闭所有端口并监视外部连接尝试。当识别出预定义的尝试序列(称为敲门序列 )时,可以执行打开端口等操作,让外部的用户能够进来。敲门序列的复杂程度由您决定,从简单的列表(比如依次尝试 TCP 端口 7000、UDP 端口 7100 和 TCP 端口 7200)到一次性序列集合都可以。(按密码学术语来说,一次性序列与 “一次一密” 相似,这是已知最安全的加密方法。)外面的用户必须知道使用 SSH 所需的端口号和密码,还必须知道打开端口并启用密码所需的敲门序列。如果没有这个序列,连接尝试就会静悄悄地失败。
这为什么是非常安全的方案?因为有 65,535 个端口(见 参考资料)。即使考虑到已经分配的端口,仍然有超过 60,000 个可用端口。如果敲门序列只包含四次 “敲门”,黑客要想通过强力攻击猜出序列,就必须测试大约 13,000,000,000,000,000,000 个序列(13 后面 18 个零)。这样的攻击显然不太可能奏效!当然,强力攻击或胡乱猜测并不是猜出正确序列的惟一方法。因此,不要只使用单一安全方法;而是使用一系列安全层来增加攻击的难度。
必须安装敲门守护进程 knockd
;它监视敲门序列,当发现有效的序列时执行相应的操作。如果愿意,可以从头构建它,但是大多数(如果不是所有的话)发行版中都有这个包。最好使用包管理工具安装它。例如,在 OpenSUSE 中,可以使用 Yast2
或通过执行 sudo zypper install knockd
安装它。在 Ubuntu 中可以使用 sudo apt-get install knockd
,在 Debian 中使用 sudo aptitude install knockd
。用发行版的软件安装工具搜索 knockd 往往能够找到它。
安装这个包之后,必须编辑 /etc/knockd.conf 文件以指定端口敲门规则,然后启动守护进程。为了完成所需的设置,必须了解您的防火墙的工作方式。例如,在 OpenSUSE 中,可以使用 清单 5 这样的设置。
清单 5. 针对 OpenSUSE 防火墙设计的示例配置文件1234567[opencloseSSH] sequence= 7000,8000,9000 tcpflags= syn seq_timeout= 15 cmd_timeout= 30 start_command= /usr/sbin/iptables -s %IP% -I input_ext 1 -p tcp --dport 22960 -j ACCEPT stop_command= /usr/sbin/iptables -s %IP% -D input_ext -p tcp --dport 22960 -j ACCEPT
这个示例在用户依次在端口 7000、8000 和 9000 上敲门之后启用 SSH 访问。
在启动 knockd
之前,关闭端口 22960 并尝试远程登录。这个尝试应该会失败,见清单 6。
清单 6. 如果禁用 SSH 访问而且不启动敲门守护进程,登录尝试会失败12> ssh the.url.for.your.site -p 22960 -o ConnectTimeout=15 ssh: connect to host the.url.for.your.site port 22960: Connection timed out
现在,使用 sudo /etc/init.d/knockd start
或 sudo knockd -d
启动端口敲门守护进程(这两个命令是等效的),然后再试一下;端口敲门序列要求在端口 7000、8000 和 9000 上敲门。必须在 15 秒内完成这个序列。识别出序列之后端口打开,必须在 30 秒内登录。否则,端口再次关闭。
为了检验这个过程,回到您的远程机器上并登录。这一次提供所需的敲门序列,见 清单 7。注意,在安装 knockd
时通常也会安装 knock
命令。如果不是这样,只需用发行版的包管理工具搜索它。
清单 7. 提供所需的敲门序列之后登录成功12345> knock the.url.for.your.site 7000 > knock the.url.for.your.site 8000 > knock the.url.for.your.site 9000 > ssh the.url.for.your.site -p 22960 -o ConnectTimeout=10 Password:
如果提供了错误的敲门序列(或根本没有敲门),会收到 “Connection timed out” 消息,SSH 端口仍然完全关闭,看不出它是存在的。
如果您处于路由器后面
如果您的服务器通过路由器连接 Internet,就必须修改它的配置。具体细节因路由器和防火墙类型而异,但是一般来说应该:
- 打开敲门端口并把数据包转发到您的计算机,让
knockd
能够识别并处理它们。 - 把端口 22960(SSH 连接使用的端口)上的数据包转发到您计算机上的端口 22960。
- 配置您计算机的防火墙,让它拒绝对端口 22960 和敲门端口的连接。
尽管路由器会打开一些端口,但是对它们的所有访问都会到达您计算机的防火墙。访问会被阻止,除非探测到正确的端口敲门序列。
敲门配置
/etc/knockd.conf 文件有一个一般选项小节 options
,希望使用的每个敲门序列各有一个小节。选项可以是大写、小写或大小写混合形式。
- 在默认情况下,
knockd
监视 eth0 接口。要想使用另一个接口(例如 eth1),可以包含Interface=eth1
行。注意,只使用设备名而不是设备的完整路径。 - 如果希望启用日志记录,可以通过包含
useSyslog
行使用标准的 Linux 日志文件,也可以通过包含LogFile=/the/full/path/to/your/file
使用自己的文件。但是,应该认识到日志记录是一个漏洞;如果黑客获得了日志,入侵者就会掌握端口敲门序列。 - 如果希望能够检查
knockd
是否仍然在运行,那么包含PidFile=/the/full/path/to/a/PID/file
。这个守护进程的进程 ID (PID) 将存储在这个文件中。应该通过一个cron
任务定期检查knockd
是否仍然在运行并在需要时重新启动它。注意,当这个守护进程停止运行时,系统是安全的;所有端口关闭,不可访问。在守护进程重新启动之前,用户无法登录。
可以让 knockd
监听多个序列并以不同方式响应各个序列。在前面的示例中,让 knockd
打开 SSH 端口;可以简单地启用 HTTP 端口,让用户能够访问 web 服务器,也可以运行特定的进程。在配置文件中,每个序列都有相应的小节。
- 使用
sequence
定义敲门序列,比如7000,8000,9000
。在默认情况下,敲门使用 TCP,但是可以添加 UDP 以增加复杂性,比如7000,8000:udp,9000
。 - 除了使用固定的序列之外,还可以指定一个包含 “一次性序列” 的文件,这些序列在使用之后就会删除,不能再次使用。指定这种序列的方法如下:
1
one_time_sequences=/the/full/path/to/a/sequences/file
使用任何文本编辑器创建此文件;其中每行包含一个序列(按照上面所示的格式)。应该在远程计算机上保存此文件的拷贝以便记住如何登录。
- 可以指定应该扫描哪些到达的 TCP 数据包,丢弃不与
ACK
、FIN
、PSH
、RST
、SYN
或URG
标志匹配的数据包。对于 SSH 连接,应该使用TCPFlags=SYN
。 - 可以用
Seq_Timeout=seconds.to.wait
指定完成一个序列的最大时间。如果在此时间内没有输入完整的序列,就不会识别出它,访问被拒绝。 - 可以用
Cmd_Timeout=seconds.to.wait
指定在识别出序列之后用户执行第二个命令的最大时间。如果提供了敲门序列的用户没有快速地输入下一个命令(例如登录),端口会再次关闭。 - 最重要的参数是
Start_command=some.command.to.execute
,它指定成功地识别出敲门序列之后要执行的命令或脚本。如果需要引用敲门者的 IP 地址(例如为了允许从他的计算机连接您的计算机),可以使用%IP%
。在运行时,它会替换为正确的值。在上面的示例中指定:1/usr/sbin/iptables -s %IP% -I input_ext 1 -p tcp --dport 22960 -j ACCEPT
iptables
向提供敲门序列的 IP 地址上的用户开放端口 22960。 - 另一个重要的参数是
Stop_command=some.command.to.execute
;当超过Cmd_timeout
时间之后,执行它指定的命令或脚本。
在这里,因为只希望打开或关闭端口 22960,所以使用单一命令就够了。如果需要更复杂的操作,可以通过调用脚本执行所需的任何操作 — 操作甚至可以完全不涉及打开端口。可以触发任何操作,比如运行进程或执行备份。当然,了解要使用的命令可能有点儿难度。例如,因为我运行 OpenSUSE,它提供自己的防火墙前端,所以我不得不通过查看 iptables -l
的输出了解应该执行哪个命令来打开或关闭端口 22960。
对于 knockd
本身,有几个选项需要考虑:
-c
:允许指定默认配置文件 /etc/knockd.conf 之外的另一个配置文件-d
:让knockd
作为后台守护进程运行,这是标准运行方式-D
:提供输出调试消息-h
:提供关于语法和选项的帮助-i
:允许指定默认的eth0
接口之外的其他接口-l
:为日志项启用 DNS 查找 — 这是一种不好的做法,因为这强迫计算机使用 DNS 通信流,会产生漏洞-v
:提供更详细的消息和解释-V
:提供程序的版本号
最后,可以使用多种方法产生敲门序列本身,编写 knock
命令是最简单的方法。
以下命令在 TCP 端口 7000 上敲门:
1
|
knock the.url.for.your.site 7000 |
以下命令在 UDP 端口 8000 上敲门:
1
|
knock the.url.for.your.site -u 8000 |
或
1
|
knock the.url.for.your.site 8000:udp |
-h
参数提供这个命令的帮助。
结束语
您看到了三种保护 SSH 访问的方法:修改 sshd 的配置参数,通过 PAM 限制可以登录的用户,以及使用端口敲门序列隐藏存在 SSH 访问的事实。尽管没有任何方法能够完全保护任何计算机,但是采取这三个措施会让您的服务器安全一些。
保护 SSH 的三把锁的更多相关文章
- 如何增加黑客通过ssh入侵的难度--保护ssh的三把锁
源文档:https://blog.csdn.net/cnbird2008/article/details/6130696 简介 如果需要远程访问计算机并启用了 Secure Shell (SSH) 连 ...
- 保护SSH的三把锁
///////////////////////////////写在前面//////////////////////////////////////原帖地址:http://www.ibm.com/dev ...
- 并发编程之 Java 三把锁
前言 今天我们继续学习并发.在之前我们学习了 JMM 的知识,知道了在并发编程中,为了保证线程的安全性,需要保证线程的原子性,可见性,有序性.其中,synchronized 高频出现,因为他既保证了原 ...
- java对象头信息和三种锁的性能对比
java头的信息分析 首先为什么我要去研究java的对象头呢? 这里截取一张hotspot的源码当中的注释 这张图换成可读的表格如下 |-------------------------------- ...
- synchronized(三) 锁的膨胀过程(锁的升级过程)深入剖析
警告⚠️:本文耗时很长,先做好心理准备................哈哈哈 本篇我们讲通过大量实例代码及hotspot源码分析偏向锁(批量重偏向.批量撤销).轻量级锁.重量级锁及锁的膨胀过程(也就是 ...
- jdk源码剖析三:锁Synchronized
一.Synchronized作用 (1)确保线程互斥的访问同步代码 (2)保证共享变量的修改能够及时可见 (3)有效解决重排序问题.(Synchronized同步中的代码JVM不会轻易优化重排序) 二 ...
- Java多线程(三)锁对象和线程池
1:锁(Lock) 1.1 java提供了一个锁的接口,这个锁同样可以达到同步代码块的功能,API文档上说使用锁比使用synchronized更加灵活. 1.2 如何使用这个“ ...
- oslab oranges 一个操作系统的实现 实验四 认识保护模式(三):中断异常
实验目的: 理解中断与异常机制的实现机理 对应章节:第三章3.4节,3.5节 实验内容: 1. 理解中断与异常的机制 2. 调试8259A的编程基本例程 3. 调试时钟中断例程 4. 建立IDT,实现 ...
- 4月26日 python学习总结 JoinableQueue、线程、三种锁
一.进程队列补充-创建进程队列的另一个类JoinableQueue JoinableQueue同样通过multiprocessing使用. 创建队列的另外一个类: JoinableQueue([max ...
随机推荐
- Docker之网络配置
目的: Docker网络配置 Docker部署SpringCloud项目 Docker网络配置 Docker网络模式介绍 Docker在创建容器时有四种网络模式:bridge/host/conta ...
- JavaScript前端和Java后端的AES加密和解密(转)
在实际开发项目中,有些数据在前后端的传输过程中需要进行加密,那就需要保证前端和后端的加解密需要统一.这里给大家简单演示AES在JavaScript前端和Java后端是如何实现加密和解密的. java端 ...
- ASP.NET SignalR 系列(六)之连接事件
本章主要介绍下SignalR自带的连接事件 其实再前面的示例中,有出现了一些事件的重载,比如 public override Task OnConnected() 下面简单介绍一下SignalR提供了 ...
- 2019 斗鱼java面试笔试题 (含面试题解析)
本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.斗鱼等公司offer,岗位是Java后端开发,因为发展原因最终选择去了斗鱼,入职一年时间了,之前面试了很多家公 ...
- vue 异步渲染
<!DOCTYPE html> <html> <head> <title> hello world vue </title> <met ...
- 珠宝juelrye英语juelrye宝石
jewellery (usually uncountable, plural jewelleries) 1.(British spelling, Canadian) Collectively, per ...
- MySQL修炼之路三
1. SQL查询 1. 执行顺序 3. select ... 聚合函数 from 表名 1. where ... 2. group by ... 4. having ... 5. order by . ...
- Yii2 路由美化
一.美化路由形式 如:localhost/index.php?r=site/index 这种路由形式对SEO不友好,那么是否可以对路由进行一下美化呢?在Yii2中我们可以将路由必成以下的形式: 如:l ...
- idea中新增package总是嵌套的解决方法
在idea中创建package,为了方便会将com.xx.xx作为一个package,下面添加对应的子package.比如service,config等.但是当我创建是总是会嵌套在下面变成了com.x ...
- 2020秋招嵌入式面经——地平线、小米、CVTE、大华、绿米
地平线提前批 一面凉: 投递简历过程:官网投递9月份之前投的都是提前批,投了北京.上海.南京地区的嵌入式软件研发岗,北京和上海的都被筛掉了,南京的捞了我,hr小姐姐打电话邀约面试. 一面凉: 电话面试 ...