Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
家好,我是 Echa。
依賴注入 DI (Dependency Injection) 是編程領(lǐng)域中一個非常常見的設(shè)計模式,它指的是將應(yīng)用程序所需的依賴關(guān)系(如服務(wù)或其他組件)通過構(gòu)造函數(shù)參數(shù)或?qū)傩宰詣幼⑷氲倪^程。這樣做的好處是可以減少組件之間的耦合,更容易測試和維護。
我們先舉個簡單的例子,我們有兩個簡單的 A 類和 B 類,在 B 類中依賴了 A 類,我們在 B 類中對它進行實例化,并調(diào)用它的方法:
class A {
constructor(name) {
this.name = name;
}
log() {
console.log("name: ", this.name);
}
}
class B {
a = new A("Echa");
start() {
this.a.log();
}
}
const b = new B();
b.start();
但是這種寫法是非常不靈活的, A 類作為一個依賴項,它的初始化的邏輯被硬編碼到了 B 類中,如果我們想添加或修改其他的依賴項,必須要不斷修改 B 類。
借助依賴注入的設(shè)計思想,我們可以將代碼改寫成下面這樣:
class A {
constructor(name) {
this.name = name;
}
log() {
console.log("name: ", this.name);
}
}
class B {
constructor(a) {
this.a = a;
}
start() {
this.a.log();
}
}
const a = new A();
const b = new B(a);
b.start();
代碼只做了很小的改動,最核心的變化就是我們將 A 類和 B 的實現(xiàn)完全分離開來了,他們無需再關(guān)心依賴的實例化,因為我們將依賴的注入提到的最外側(cè)。
這也就是為什么我們常常將依賴注入和控制反轉(zhuǎn) IoC (Inversion of Control) 放在一起講,控制反轉(zhuǎn)即將創(chuàng)建對象的控制權(quán)進行轉(zhuǎn)移,以前創(chuàng)建對象的主動權(quán)和創(chuàng)建時機是由自己把控的,而現(xiàn)在這種權(quán)力轉(zhuǎn)移到第三方。
可能在這樣簡單的代碼中我們還看不出來什么好處,但是在大型的代碼庫中,這種設(shè)計可以顯著幫助我們減少樣板代碼,創(chuàng)建和連接依賴項的工作由一段程序統(tǒng)一處理,我們無需擔心創(chuàng)建特定類所需的類的實例。
在 JavaScript 的各大框架中,依賴注入的設(shè)計模式也發(fā)揮著非常重要的作用,在 Angular、Vue.js、Next.js 等框架中都用到了依賴注入的設(shè)計模式。
在 Angular 中大量應(yīng)用了依賴注入的設(shè)計思想。Angular 使用依賴注入來管理應(yīng)用的各個部分之間的依賴關(guān)系,以及如何將這些依賴關(guān)系注入到應(yīng)用中,例如你可以使用依賴注入來注入服務(wù)、組件、指令、管道等。
比如我們現(xiàn)在有個日志打點的工具類,我們可以使用 Injectable 將其指定為可注入對象。
// logger.service.ts
import { Injectable } from '@angular/core';
@Injectable({providedIn: 'root'})
export class Logger {
writeCount(count: number) {
console.warn(count);
}
}
然后在組件中使用時,無需進行實例化,直接在 constructor 的參數(shù)中就可以取出自動注入好的對象:
// hello-world-di.component.ts
import { Component } from '@angular/core';
import { Logger } from '../logger.service';
@Component({
selector: 'hello-world-di',
templateUrl: './hello-world-di.component.html'
})
export class HelloWorldDependencyInjectionComponent {
count = 0;
constructor(private logger: Logger) { }
onLogMe() {
this.logger.writeCount(this.count);
this.count++;
}
}
在 Vue.js 中,provide 和 inject 其實也使用了依賴注入的設(shè)計模式。
export default {
name: 'Parent',
provide() {
return {
user: this.user
};
},
data() {
return {
user: {
name: 'John',
age: 30
}
};
}
};
// 子組件
export default {
name: 'Child',
inject: ['user'],
computed: {
userName() {
return this.user.name;
}
}
};
在 React.js 中,并沒有直接使用依賴注入的地方,不過我們依然可以借助一些第三方庫來實現(xiàn), 比如我們可以通過 InversifyJS 提供的 injectable decorator 標記 class 是可被注入的。
import { injectable } from "inversify";
export interface IProvider<T> {
provide(): T;
}
@injectable()
export class NameProvider implements IProvider<string> {
provide() {
return "World";
}
}
在組件中,我們可以直接調(diào)用注入的 provide 方法,而組件內(nèi)部不用關(guān)心它的實現(xiàn)。
import * as React from "react";
import { IProvider } from "./providers";
export class Hello extends React.Component {
private readonly nameProvider: IProvider<string>;
render() {
return <h1>Hello {this.nameProvider.provide()}!</h1>;
}
}
前面我們提到的 InversifyJS 實際上就是一個專門用來實現(xiàn)依賴注入的工具庫,它主要就由 injectable 、inject 等幾個裝飾器組成的,這么神奇的功能究竟是咋實現(xiàn)的呢,下面我們手動來實現(xiàn)一下。
首先我們來明確一個需求場景,假設(shè)我們要使用 Koa 框架開發(fā)一個簡單的 Node.js 服務(wù)。
在 Koa 中,Controller 用來處理用戶請求和響應(yīng),它負責接收用戶的請求,然后調(diào)用相應(yīng)的服務(wù)或業(yè)務(wù)邏輯進行處理,最后將處理結(jié)果返回給用戶。Service 用來封裝業(yè)務(wù)邏輯和數(shù)據(jù)處理,它負責實現(xiàn)應(yīng)用程序的核心功能。
Service 通常會被多個 Controller 所調(diào)用,它們之間是松散耦合的關(guān)系,我們希望用兩裝飾器來實現(xiàn) Service 的自動依賴注入:
export default class UserController extends Controller {
@Inject
user: UserService;
@UseService
async list(ctx: ThriftContext): Promise<void> {
const user = await this.user.findAll({ id: 1000 });
console.log(1, user);
}
}
在實現(xiàn)過程中我們可能會用到兩個非常重要的 API,Metadata Reflection API 以及 Decorator API,我們先分別來回顧一下它們的基礎(chǔ)知識。
裝飾器模式是一種經(jīng)典的設(shè)計模式,其目的是在不修改被裝飾者(如某個函數(shù)、某個類等)源碼的前提下,為被裝飾者增加 / 移除某些功能。一些現(xiàn)代編程語言在語法層面提供了對裝飾器模式的支持,并且各語言中的現(xiàn)代框架都大量應(yīng)用了裝飾器。主要用處分為兩大類:
我們目前用的比較多的裝飾器就是 TypeScript 的實驗性裝飾器,以及 ECMAScript中還處于 legacy 階段的 Decorator API,下面是它的用法:
裝飾類的時候,裝飾器方法一般會接收一個目標類作為參數(shù),下面是一個示例,給類增加靜態(tài)屬性、原型方法:
const addField = target => {
target.age = 17;
target.prototype.speak = function () {
console.log('xxx');
};
};
@addField
class People {
}
console.log(People.age);
const a = new People();
a.speak();
類屬性裝飾器可以用在類的屬性、方法、get/set 函數(shù)中,一般會接收三個參數(shù):
下面是一個示例,可以修改類屬性為只讀:
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Person {
@readonly name = 'person'
}
const person = new Person();
person.name = 'tom';
Reflect 是 JavaScript 中的一個內(nèi)置對象,它提供了一組用于操作對象的方法。它與其他內(nèi)置對象類似,但是它的目的是為了提供一組用于操作對象的通用方法。
Reflect Metadata 是 ES7 的一個提案,它主要用來在聲明的時候添加和讀取元數(shù)據(jù)。
Reflect.getMetadata('design:type', target, key) 可以用來獲取類 target 中屬性 key 的類型信息:
function Inject() {
return function (target: any, key: string, descriptor: PropertyDescriptor) {
const type = Reflect.getMetadata('design:type', target, key);
console.log(type); // [class Service]
return descriptor;
};
}
export default class WebsiteController extends Controller {
@Inject()
service: Service
// ...
}
Reflect.getMetadata('design:paramtypes', target, key) 可以用來獲取類 target 中屬性 key 的函數(shù)參數(shù)類型;
Reflect.getMetadata('design:returntype', target, key) 可以用來獲取類 target 中屬性 key 的函數(shù)返回值類型。
除能獲取固定的類型信息之外,也可以自定義 MetaData,并在合適的時機獲取它的值,示例如下:
function classDecorator(): ClassDecorator {
return target => {
// 在類上定義元數(shù)據(jù),key 為 `classMetaData`,value 為 `a`
Reflect.defineMetadata('classMetaData', 'a', target);
};
}
@classDecorator()
class SomeClass {
}
Reflect.getMetadata('classMetaData', SomeClass); // 'a'
好了,有了這些知識,我們就可以手動來實現(xiàn)一個依賴注入裝飾器了。
再明確一下我們的需求:在不同服務(wù)的 Controller 中共用 Service,使用 Service 時可以自動獲取已注入的 Service 實例,同時 Service 里可以獲取到請求的 Context 信息。
首先我們來實現(xiàn),Inject 裝飾器:
function Inject(target: any, key: string) {
console.log(`注冊 Controller: ${target} Service: ${key}`);
// 獲取當前 Service 的類型
const serviceClass = Reflect.getMetadata('design:type', target, key);
// 獲取當前 Controller 已經(jīng)注冊過的 Service List
const serviceList = Reflect.getMetadata(META_KEY_CONTROLLER_SERVICE, target) || [];
// 將當前 Service 進行追加
Reflect.defineMetadata(
META_KEY_CONTROLLER_SERVICE,
[...serviceList, { serviceClass, serviceName: key }],
target
);
}
然后是 UseService 裝飾器:
function UseService(target: any, name: string, descriptor: PropertyDescriptor) {
const value = descriptor.value;
descriptor.value = async function (...args: any) {
// 獲取當前請求的 Context
const [ctx] = args;
// 取出當前 Controller 已綁定的 Service
const serviceList = Reflect.getMetadata(META_KEY_CONTROLLER_SERVICE, target) || [];
console.log(serviceList);
for (let i = 0; i < serviceList.length; i++) {
const { serviceClass, serviceName } = serviceList[i];
// 實例化 Service 并綁定 Context
const service = new serviceClass(ctx);
Reflect.set(service, 'ctx', ctx);
// 給當前 Controller 掛載 Service 實例
Reflect.set(target, serviceName, service);
}
return await Promise.resolve(value.apply(this, args));
};
return descriptor;
}
好了,接下來就可以愉快的使用了~
export default class UserController extends Controller {
@Inject
user: UserService;
@UseService
async list(ctx: ThriftContext): Promise<void> {
const user = await this.user.findAll();
console.log(1, user);
}
}
如果這篇文章幫助到了你,歡迎點贊和關(guān)注。
019年10月,貴陽某高校網(wǎng)站遭遇黑客攻擊,登錄頁面被非法篡改為違法有害信息。警方根據(jù)《中華人民共和國網(wǎng)絡(luò)安全法》第21條、 第59條之規(guī)定,依法對該高校及高校負責人分別處以10萬元和5萬元的行政罰款。
網(wǎng)站被攻擊了,網(wǎng)站負責人還要被處罰,這到底是怎么回事?
實際上,《網(wǎng)絡(luò)安全法》規(guī)定,網(wǎng)站運營者應(yīng)負擔網(wǎng)站網(wǎng)絡(luò)安全保護義務(wù)。
近年來,網(wǎng)絡(luò)安全形勢日趨嚴峻,威脅持續(xù)上升。為檢驗企事業(yè)單位關(guān)鍵信息基礎(chǔ)設(shè)施安全防護能力,提升網(wǎng)絡(luò)安全應(yīng)急處置隊伍應(yīng)對能力,自2016年起,我國每年都會定期開展“HW行動”,以全國范圍內(nèi)的真實網(wǎng)絡(luò)目標為對象進行實戰(zhàn)攻防活動。
今年是建黨100周年,也是COP15(聯(lián)合國《生物多樣性公約》第十五次締約方大會)的召開之年。新一輪的“HW行動”近期正在開展,我省安全主管部門加大了對各類網(wǎng)站的檢測力度,嚴查可能出現(xiàn)的網(wǎng)絡(luò)安全風險問題。
近一個月,藍隊云已經(jīng)收到了多家被通報企業(yè)的緊急求助,這些網(wǎng)站或多或少都被檢測出了不同類型的漏洞風險,并要求在規(guī)定時間內(nèi)完成整改。
大部分被通報的網(wǎng)站會面臨兩種類型的漏洞。
1、軟件代碼漏洞
簡單來說就是自行開發(fā)或使用的第三方公司提供的軟件存在漏洞,這一類的漏洞一般是軟件開發(fā)者代碼編寫不規(guī)范造成的隱患,或者是編程語言的局限性導(dǎo)致的漏洞,軟件代碼漏洞可能會被入侵者利用。
2、服務(wù)器漏洞
在硬件、軟件、協(xié)議的具體實現(xiàn)或系統(tǒng)安全策略上存在的缺陷,從而可以使攻擊者能夠在未授權(quán)的情況下訪問或破壞系統(tǒng)。一般服務(wù)器供應(yīng)商只提供計算環(huán)境和網(wǎng)絡(luò)帶寬服務(wù),服務(wù)器的密碼和系統(tǒng)安全設(shè)置都掌握在用戶自己手中,服務(wù)器安全依賴于使用主體對服務(wù)器的安全部署。
藍隊云根據(jù)近幾年在“HW行動”中被通報的網(wǎng)站漏洞情況,將常見漏洞做了以下整理:
1、跨站腳本XSS
黑客通過“HTML注入”篡改了網(wǎng)頁,插入了惡意腳本,從而在用戶在瀏覽網(wǎng)頁時,實現(xiàn)控制用戶瀏覽器行為的一種攻擊方式。
2、SQL注入
SQL注入是比較常見的網(wǎng)絡(luò)攻擊方式之一,它不是利用操作系統(tǒng)的BUG來實現(xiàn)攻擊,而是針對程序員編寫時的疏忽,通過SQL語句,實現(xiàn)無賬號登錄,甚至篡改數(shù)據(jù)庫。
3、WEB服務(wù)器漏洞
服務(wù)器解析漏洞依然廣泛存在,常見Web服務(wù)器的解析漏洞有Apache/Nginx/IIS等。
4、數(shù)據(jù)庫漏洞
常見的數(shù)據(jù)庫漏洞主要有Oracle/MySQL兩大類。內(nèi)外部黑客會想法利用管理、網(wǎng)絡(luò)、主機或數(shù)據(jù)庫的自身漏洞嘗試入侵到數(shù)據(jù)庫中,以達到自身的目的。
5、弱口令
弱口令指的是僅包含簡單數(shù)字和字母的口令,例如“123”、“abc”等,因為這樣的口令很容易被別人破解,通過系統(tǒng)弱口令,可被黑客直接獲得系統(tǒng)控制權(quán)限。
6、信息泄漏漏洞
最常見的信息泄漏包含電子郵件地址泄漏、數(shù)據(jù)庫信息泄漏、內(nèi)網(wǎng)IP地址泄漏、網(wǎng)站路徑泄漏風險,這類漏洞信息泄露可能是不慎泄露的,也有可能是攻擊者通過惡意的交互從網(wǎng)站獲得數(shù)據(jù)。
建議有預(yù)算的企事業(yè)單位、社會機構(gòu)盡早進行安全策略的部署,防患于未然。藍隊云為云上用戶提供基礎(chǔ)的安全策略配置,可有效提升服務(wù)器的安全級別,同時附送5G基礎(chǔ)流量型DDOS攻擊防御;當然也可以請安全專家來進行整體加固,防范系統(tǒng)層和應(yīng)用層的安全風險。
如果遇到自己處理不了的網(wǎng)站漏洞,也可以隨時隨地找隊長免費救援,針對在護網(wǎng)中遇到“困難”的企業(yè),我們可免費提供專家級風險評估服務(wù),幫助您點對點處理問題。
京東SRC(Security Response Center)收錄大量外部白帽子提交的sql注入漏洞,漏洞發(fā)生的原因多為sql語句拼接和Mybatis使用不當導(dǎo)致。
mysql5.0以上版本中存在一個重要的系統(tǒng)數(shù)據(jù)庫information_schema,通過此數(shù)據(jù)庫可訪問mysql中存在的數(shù)據(jù)庫名、表名、字段名等元數(shù)據(jù)。information_schema中有三個表成為了sql注入構(gòu)造的關(guān)鍵。
SQL注入常用SQL函數(shù)
// sqli vuln code
Statement statement = con.createStatement();
String sql = "select * from users where username = '" + username + "'";
logger.info(sql);
ResultSet rs = statement.executeQuery(sql);
// fix code 如果要使用原始jdbc,請采用預(yù)編譯執(zhí)行
String sql = "select * from users where username = ?";
PreparedStatement st = con.prepareStatement(sql);
使用未預(yù)編譯原始jdbc作為demo,注意此demo中sql語句參數(shù)采用單引號閉合。
對于字符類型注入,通常先嘗試單引號,判斷單引號是否被拼接到SQL語句中。推薦使用瀏覽器擴展harkbar作為手工測試工具。https://chrome.google.com/webstore/detail/hackbar/ginpbkfigcoaokgflihfhhmglmbchinc
正常頁面應(yīng)該顯示如下:
admin后加單引號導(dǎo)致無信息回顯,原因是后端sql執(zhí)行報錯,說明引號被拼接至SQL語句中
select * from users where username = 'admin' #正常sql
select * from users where username = 'admin'' #admin'被帶入sql執(zhí)行導(dǎo)致報錯無法顯示信息
mysql中使用order by 進行排序,不僅可以是字段名也可以是字段序號。所以可以用來判斷表中字段數(shù),order by 超過字段個數(shù)的數(shù)字就會報錯。
判斷字段數(shù)
當order by 超過4時會報錯,所以此表共四個字段。
后端所執(zhí)行的sql語句
select * from users where username = 'admin' order by 1-- '
此處我們將原本username的值admin替換為admin’ order by 1 —+,其中admin后的單引號用于閉合原本sql語句中的前引號,—+用于注釋sql語句中的后引號。—后的+號主要作用是提供一個空格,sql語句單行注釋后需有空格,+會被解碼為空格。
主要用于定位后端sql字段在前端顯示的位置,采用聯(lián)合查詢的方式確定。注意聯(lián)合查詢前后字段需一致,這也就是我們?yōu)槭裁醋龅诙降脑颉?/span>
通過下圖可知,后端查詢并回顯的字段位置為2,3位。
聯(lián)合查詢后的字段可以隨意,本次采用的是數(shù)字1到4直觀方便。
group_concat()函數(shù)用于將查詢結(jié)果拼接為字符串。
sqlmap兼容python2和python3,可以自動化檢測各類注入和幾乎所有數(shù)據(jù)庫類型。
-u 可能存在注入的url鏈接
-r讀取http數(shù)據(jù)包
--data 指定post數(shù)據(jù)
--cookie 指定cookie
--headers 指定http頭 如采用token認證的情況下
--threads 指定線程數(shù)
--dbms 指定后端的數(shù)據(jù)庫
--os 指定后端的操作系統(tǒng)類型
--current-user 當前用戶
--users 所有用戶
--is-dba 是否是dba
--sql-shell 交互式的sqlshell
-p指定可能存在注入點的參數(shù)
--dbs 窮舉系統(tǒng)存在的數(shù)據(jù)庫
-D指定數(shù)據(jù)庫
--tables 窮舉存在的表
-T指定表
--column 窮舉字段
-C指定字段
--dump dump數(shù)據(jù)
直接檢測
其中—cookie用于指定cookie,—batch 自動化執(zhí)行,—dbms指定數(shù)據(jù)庫類型
檢測結(jié)果
讀取系統(tǒng)中存在數(shù)據(jù)庫
—dbs讀取當前用戶下的數(shù)據(jù)庫
讀取指定庫下的表
-D java_sec_code —tables
dump users表數(shù)據(jù)
-D java_sec_code -T users —dump
//采用#不會導(dǎo)致sql注入,mybatis會使用預(yù)編譯執(zhí)行
@Select("select * from users where username = #{username}")
User findByUserName(@Param("username") String username);
//采用$作為入?yún)⒖蓪?dǎo)致sql注入
@Select("select * from users where username = '${username}'")
List<User> findByUserNameVuln01(@Param("username") String username);
//錯誤寫法
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like '%${_parameter}%'
</select>
//正確寫法
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like concat(‘%’,#{_parameter}, ‘%’)
</select>
order by 后若使用#{}會導(dǎo)致報錯,因為#{}默認添加引號會導(dǎo)致找不到字段從而報錯。
//錯誤寫法
<select id="findByUserNameVuln03" parameterType="String" resultMap="User">
select * from users
<if test="order != null">
order by ${order} asc
</if>
</select>
//正確寫法 id指字段id 此表字段共四個 所以id為1-4
<select id="OrderByUsername" resultMap="User">
select * from users order by id asc limit 1
</select>
slqmap手冊:https://octobug.gitbooks.io/sqlmap-wiki-zhcn/content/Users-manual/Introduction.html
sql注入詳解:http://sqlwiki.radare.cn/#/
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。