fortify問題處理(JSON Injection媚赖、Cross-Site Scrpting漏隐、Path Manipulation)

作者 描述 時間
雨中星辰 2021-03-29 記錄項目中遇到的fortify問題及處理辦法

JSON Injection(json注入)

  • 摘要
    該方法會將未經(jīng)驗證的輸入寫入 JSON狈孔。攻擊者可以利用此調(diào)用將任意元素或?qū)傩宰⑷?JSON 實體滞谢。
  • 解釋

JSON injection 會在以下情況中出現(xiàn):

  1. 數(shù)據(jù)從一個不可信賴的數(shù)據(jù)源進入程序。
  2. 將數(shù)據(jù)寫入到 JSON流除抛。 應(yīng)用程序通常使用 JSON 來存儲數(shù)據(jù)或發(fā)送消息狮杨。用于存儲數(shù)據(jù)時, JSON 通常會像緩存數(shù)據(jù)那樣處理到忽, 而且可能會包含敏感信息橄教。用于發(fā)送消息時清寇, JSON 通常與 RESTful 服務(wù)一起使用,并且可以用于傳輸敏感信息护蝶,例如身份驗證憑據(jù)华烟。 如果應(yīng)用程序利用未經(jīng)驗證的輸入構(gòu)造 JSON,則可以更改 JSON 文檔和消息的語義持灰。在相對理想的情況下盔夜,攻擊者可能會插入無關(guān)的元素,導(dǎo)致應(yīng)用程序在解析 JSON 文檔或請求時拋出異常堤魁。在更為嚴(yán)重的情況下喂链,例如涉及 JSON Injection,攻擊者可能會插入無關(guān)的元素妥泉,從而允許對JSON 文檔或請求中對業(yè)務(wù)非常關(guān)鍵的值執(zhí)行可預(yù)見操作椭微。還有一些情況, JSON Injection 可以導(dǎo)致 CrossSite Scripting 或 Dynamic Code Evaluation盲链。

例 1: 以下 Java 代碼使用 Jackson 將非特權(quán)用戶(這些用戶具有“默認(rèn)”角色蝇率,與之相反,特權(quán)用戶具有“管理員”角色)的用戶帳戶身份驗證信息從用戶控制的輸入變量

username 和 password 序列化為位于 ~/user_info.json 的 JSON 文件:

JsonFactory jfactory = new JsonFactory();
JsonGenerator jGenerator = jfactory.createJsonGenerator(new File("~/
user_info.json"), JsonEncoding.UTF8);
jGenerator.writeStartObject();
jGenerator.writeFieldName("username");
jGenerator.writeRawValue("\"" + username + "\"");
jGenerator.writeFieldName("password");
jGenerator.writeRawValue("\"" + password + "\"");
jGenerator.writeFieldName("role");
jGenerator.writeRawValue("\"default\"");
jGenerator.writeEndObject();
jGenerator.close();

但是刽沾,由于 JSON 序列化使用 JsonGenerator.writeRawValue() 來執(zhí)行本慕,將不會對 username 和
password 中的不可信賴數(shù)據(jù)進行驗證以轉(zhuǎn)義與 JSON 相關(guān)的特殊字符。這樣侧漓,用戶就可以任意插入 JSON
密鑰间狂,可能會更改已序列化的 JSON 的結(jié)構(gòu)。在本例中火架,在設(shè)置 username 的值的提示符下輸入用戶名時鉴象,
如果非特權(quán)用戶 mallory(密碼為 Evil123!)將 ","role":"admin 附加到其用戶名中,則最終保存到
~/user_info.json 的 JSON 將為:

{
"username":"mallory",
"role":"admin",
"password":"Evil123!",
"role":"default"
}

如果隨后將此序列化 JSON 文件反序列化為 HashMap 對象何鸡,其中 Jackson 的 JsonParser 如下所示:

  JsonParser jParser = jfactory.createJsonParser(new File("~/user_info.json"));
  while (jParser.nextToken() != JsonToken.END_OBJECT) {
    String fieldname = jParser.getCurrentName();
    if ("username".equals(fieldname)) {
      jParser.nextToken();
    userInfo.put(fieldname, jParser.getText());
    }
    if ("password".equals(fieldname)) {
      jParser.nextToken();
    userInfo.put(fieldname, jParser.getText());
    }
    if ("role".equals(fieldname)) {
      jParser.nextToken();
    userInfo.put(fieldname, jParser.getText());
    }
    if (userInfo.size() == 3)
      break;
  }
  jParser.close();

HashMap 對象中 username纺弊、 passwordrole 密鑰的最終值將分別為 malloryEvil123!admin骡男。 在沒有進一步驗證反序列化 JSON 值是否有效的情況下淆游,應(yīng)用程序會錯誤地為用戶mallory 分配 管理員特權(quán)。

  • 解決辦法
    引入依賴
<dependency>
  <groupId>com.mikesamuel</groupId>
  <artifactId>json-sanitizer</artifactId>
  <version>1.2.2</version>
</dependency>

使用方法com.google.json.JsonSanitizer類的public static String sanitize(String jsonish)對json字符串進行一個預(yù)處理隔盛,避免json注入犹菱。
例:

  JsonSanitizer.sanitize(json)

Cross-Site Scrpting(xss)跨站腳本漏洞

  • 摘要
    向一個 Web 瀏覽器發(fā)送未經(jīng)驗證的數(shù)據(jù)會導(dǎo)致該瀏覽器執(zhí)行惡意代碼。
  • 描述
    Cross-Site Scripting (XSS) 漏洞在以下情況下發(fā)生:
  1. 數(shù)據(jù)通過一個不可信賴的數(shù)據(jù)源進Web 應(yīng)用程序吮炕。 對于基于 DOM 的 XSS腊脱,將從 URL 參數(shù)或瀏覽器中的其他值讀取數(shù)據(jù),并使用客戶端代碼將其重新寫入該頁面龙亲。對于 Reflected XSS陕凹,不可信賴的數(shù)據(jù)源通常為 Web 請求悍抑,而對于 Persisted(也稱為 Stored)XSS,該數(shù)據(jù)源通常為數(shù)據(jù)庫或其他后端數(shù)據(jù)存儲杜耙。
  2. 未檢驗包含在動態(tài)內(nèi)容中的數(shù)據(jù)搜骡,便將其傳送給了Web 用戶。對于基于 DOM 的 XSS佑女,任何時候當(dāng)受害人的瀏覽器解析 HTML 頁面時记靡,惡意內(nèi)容都將作為DOM(文檔對象模型)創(chuàng)建的一部分執(zhí)行。 傳送到 Web 瀏覽器的惡意內(nèi)容通常采用 JavaScript 代碼片段的形式团驱,但也可能會包含一些HTML摸吠、 Flash 或者其他任意一種可以被瀏覽器執(zhí)行的代碼〉瓴瑁基于 XSS 的攻擊手段花樣百出蜕便,幾乎是無窮無盡的劫恒,但通常它們都會包含傳輸給攻擊者的私有數(shù)據(jù)(如 Cookie 或者其他會話信息)贩幻。 在攻擊者的控制下,指引受害者進入惡意的網(wǎng)絡(luò)內(nèi)容两嘴;或者利用易受攻擊的站點丛楚,對戶的機器進行其他惡意操作。

例 1: 下面的 JavaScript 代碼片段可從 URL 中讀取雇員 ID eid憔辫,并將其顯示給用戶

<SCRIPT>
var pos=document.URL.indexOf("eid=")+4;
document.write(document.URL.substring(pos,document.URL.length));
</SCRIPT>

示例 2: 考慮使用 HTML 表單:

<div id="myDiv">
Employee ID: <input type="text" id="eid"><br>
...
<button>Show results</button>
</div>
<div id="resultsDiv">
...
</div>

下面的 jQuery 代碼片段可從表單中讀取雇員 ID趣些,并將其顯示給用戶。

$(document).ready(function(){
    $("#myDiv").on("click", "button", function(){
        var eid = $("#eid").val();
        $("resultsDiv").append(eid);
        ...
    });
});

如果雇員 ID eid 只包含標(biāo)準(zhǔn)的字母數(shù)字文本贰您,此代碼就會正常運行坏平。如果 eid 里有包含元字符或源代碼中的值,那么 Web 瀏覽器就會像顯示 HTTP 響應(yīng)那樣執(zhí)行代碼锦亦。 起初舶替,這個例子似乎是不會輕易遭受攻擊的。 畢竟杠园,有誰會輸入導(dǎo)致惡意代碼的 URL顾瞪,并且還在自己的電腦上運行呢?真正的危險在于攻擊者會創(chuàng)建惡意的 URL抛蚁,然后采用電子郵件或者社會工程的欺騙手段誘使受害者訪問此 URL 的鏈接陈醒。當(dāng)受害者單擊這個鏈接時,他們不知不覺地通過易受攻擊的網(wǎng)絡(luò)應(yīng)用程序瞧甩,將惡意內(nèi)容帶到了自己的電腦中钉跷。這種對易受攻擊的 Web 應(yīng)用程序進行盜取的機制通常被稱為反射式 XSS。 正如例子中所顯示的肚逸, XSS 漏洞是由于 HTTP 響應(yīng)中包含了未驗證的數(shù)據(jù)代碼而引起的尘应。

受害者遭受 XSS 攻擊的途徑有三種:

  1. 系統(tǒng)從 HTTP 請求中直接讀取數(shù)據(jù)惶凝,并在 HTTP 響應(yīng)中返回數(shù)據(jù)。當(dāng)攻擊者誘使用戶為易受攻擊的 Web 應(yīng)用程序提供危險內(nèi)容犬钢,而這些危險內(nèi)容隨后會反饋給用戶并在 Web 瀏覽器中執(zhí)行苍鲜,就會發(fā)生反射式 XSS 盜取。發(fā)送惡意內(nèi)容最常用的方法是玷犹,把惡意內(nèi)容作為一個參數(shù)包含在公開發(fā)表的 URL 中混滔,或者通過電子郵件直接發(fā)送給受害者。以這種手段構(gòu)造的 URL 構(gòu)成了多種“網(wǎng)絡(luò)釣魚”(phishing) 陰謀的核心歹颓,攻擊者借此誘騙受害者訪問指向易受攻擊站點的 URL坯屿。站點將攻擊者的內(nèi)容反饋給受害者以后,便會執(zhí)行這些內(nèi)容巍扛,接下來會把用戶計算機中的各種私密信息(比如包含會話信息的 cookie)傳送給攻擊者领跛,或者執(zhí)行其他惡意活動。
  2. 應(yīng)用程序?qū)⑽kU數(shù)據(jù)存儲在數(shù)據(jù)庫或其他可信賴的數(shù)據(jù)存儲器中撤奸。這些危險數(shù)據(jù)隨后會被回寫到應(yīng)用程序中吠昭,并包含在動態(tài)內(nèi)容中。Persistent XSS 盜取發(fā)生在如下情況:攻擊者將危險內(nèi)容注入到數(shù)據(jù)存儲器中胧瓜,且該存儲器之后會被讀取并包含在動態(tài)內(nèi)容中矢棚。從攻擊者的角度看,注入惡意內(nèi)容的最佳位置莫過于一個面向許多用戶府喳,尤其是相關(guān)用戶顯示的區(qū)域蒲肋。相關(guān)用戶通常在應(yīng)用程序中具備較高的特權(quán),或相互之間交換敏感數(shù)據(jù)钝满,這些數(shù)據(jù)對攻擊者來說有利用價值兜粘。如果某一個用戶執(zhí)行了惡意內(nèi)容,攻擊者就有可能以該用戶的名義執(zhí)行某些需要特權(quán)的操作弯蚜, 或者獲得該用戶個人所有的敏感數(shù)據(jù)的訪問權(quán)限孔轴。
  3. 應(yīng)用程序之外的數(shù)據(jù)源將危險數(shù)據(jù)儲存在一個數(shù)據(jù)庫或其他數(shù)據(jù)存儲器中,隨后這些危險數(shù)據(jù)被當(dāng)作可信賴的數(shù)據(jù)回寫到應(yīng)用程序中熟吏,并儲存在動態(tài)內(nèi)容中距糖。
  • 解決辦法
    使用下面兩個工具類都可以解決該問題
    public static String org.apache.commons.lang3.StringEscapeUtils.escapeHtml4(String input)
    public static String cn.hutool.http.HtmlUtil.escape(String text)
    看個人喜歡用哪個工具庫。
    maven依賴分別是:
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.10</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.5.1</version>
        </dependency>

但是對參數(shù)逐個進行預(yù)處理牵寺,效率太低悍引,不太現(xiàn)實,這里可以使用spring過濾器對請求參數(shù)帽氓,進行預(yù)處理趣斤,避免xss漏洞。

XssFilter


import java.io.IOException;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * xss攔截器
 * </p>
 *
 * @author 雨中星辰
 * @since 2020-07-10
 */
@Slf4j
//攔截所有的請求
@WebFilter(filterName = "xssFilter", urlPatterns = "/*", asyncSupported = true)
public class XssFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper(request);
        filterChain.doFilter(xssHttpServletRequestWrapper, servletResponse);
    }

    /**
     *  過濾json類型的對象解析器
     * 
     * @param builder builder
     * @return ObjectMapper
     */
    @Bean
    @Primary
    public ObjectMapper xssObjectMapper(Jackson2ObjectMapperBuilder builder) {
        // 解析器
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        // 注冊xss解析器
        SimpleModule xssModule = new SimpleModule("XssStringJsonSerializer");
        xssModule.addSerializer(new XssJacksonSerializer());
        xssModule.addDeserializer(String.class, new XssJacksonDeserializer());
        objectMapper.registerModule(xssModule);
        // 返回
        return objectMapper;
    }

}

XssHttpServletRequestWrapper

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.lang3.ArrayUtils;

import cn.hutool.http.HtmlUtil;

/**
 * <p>
 * xss請求包裝類
 * </p>
 *
 * @author 雨中星辰
 * @since 2020-07-10
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getQueryString() {
        return HtmlUtil.escape(super.getQueryString());
    }

    @Override
    public String getParameter(String name) {
        return HtmlUtil.escape(super.getParameter(name));
    }

    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (ArrayUtils.isEmpty(values)) {
            return values;
        }
        int length = values.length;
        String[] escapeValues = new String[length];
        for (int i = 0; i < length; i++) {
            escapeValues[i] = HtmlUtil.escape(values[i]);
        }
        return escapeValues;
    }

}
import java.io.IOException;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

import cn.hutool.http.HtmlUtil;

/**
 * <p>
 * xss反序列化器
 * </p>
 *
 * @author 雨中星辰
 * @since 2020-07-10
 */
public class XssJacksonDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
        throws IOException {
        return HtmlUtil.escape(jsonParser.getText());
    }

}

import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import cn.hutool.http.HtmlUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 *  xss序列化器
 * </p>
 *
 * @author 雨中星辰
 * @since 2020-07-10
 */
@Slf4j
public class XssJacksonSerializer extends JsonSerializer<String> {

    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
        throws IOException {
        jsonGenerator.writeString(HtmlUtil.escape(s));
    }

}

Path Manipulation (路徑操作)

  • 概述
    允許用戶輸入控制文件系統(tǒng)操作所用的路徑會導(dǎo)致攻擊者能夠訪問或修改其他受保護的系統(tǒng)資源黎休。
  • 詳情

當(dāng)滿足以下兩個條件時,就會產(chǎn)生 path manipulation 錯誤:

  1. 攻擊者可以指定某一文件系統(tǒng)操作中所使用的路徑。
  2. 攻擊者可以通過指定特定資源來獲取某種權(quán)限送火,而這種權(quán)限在一般情況下是不可能獲得的。

例如:在某一程序中漫仆,攻擊者可以獲得特定的權(quán)限,以重寫指定的文件或是在其控制的配置環(huán)境下運行程序泪幌。
例 1: 下面的代碼使用來自于 HTTP 請求的輸入來創(chuàng)建一個文件名盲厌。程序員沒有考慮到攻擊者可能使用像

../../tomcat/conf/server.xml”一樣的文件名,從而導(dǎo)致應(yīng)用程序刪除它自己的配置文件祸泪。
String rName = request.getParameter("reportName");
File rFile = new File("/usr/local/apfr/reports/" + rName);
...
rFile.delete();

示例 2: 以下代碼使用來自于配置文件的輸入來決定打開哪個文件吗浩,并返回給用戶。如果程序以足夠的權(quán)限
運行没隘,且惡意用戶能夠篡改配置文件懂扼,那么他們可以通過程序讀取系統(tǒng)中以擴展名 .txt 結(jié)尾的任何文件。

fis = new FileInputStream(cfg.getProperty("sub")+".txt");
amt = fis.read(arr);
out.println(arr);

有些人認(rèn)為在移動世界中右蒲,典型的漏洞(如 path manipulation)是無意義的 -- 為什么用戶要攻擊自己阀湿?但是, 謹(jǐn)記移動平臺的本質(zhì)是從各種來源下載并在相同設(shè)備上運行的應(yīng)用程序品嚣。惡意軟件在銀行應(yīng)用程序附近運行的可能性很高炕倘,它們會強制擴展移動應(yīng)用程序的攻擊面(包括跨進程通信)钧大。
例 3: 以下代碼將例 1 改編為適用于 Android 平臺翰撑。

...
String rName = this.getIntent().getExtras().getString("reportName");
File rFile = getBaseContext().getFileStreamPath(rName);
...
rFile.delete();
...

解決辦法

對文件名稱及路徑進行預(yù)處理,避免上述問題,由于項目中文件相關(guān)參數(shù)并不多啊央,這里就不適用攔截器的方式做了眶诈,采取直接對參數(shù)預(yù)處理的方式。
添加依賴

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

使用方法public static String org.apache.commons.io.FilenameUtils.normalize(String filename)入?yún)?code>文件名稱或文件路徑
例:

image.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓜饥,一起剝皮案震驚了整個濱河市逝撬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乓土,老刑警劉巖宪潮,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異趣苏,居然都是意外死亡狡相,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門食磕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尽棕,“玉大人,你說我怎么就攤上這事彬伦√舷ぃ” “怎么了伊诵?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長回官。 經(jīng)常有香客問我曹宴,道長,這世上最難降的妖魔是什么歉提? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任浙炼,我火速辦了婚禮,結(jié)果婚禮上唯袄,老公的妹妹穿的比我還像新娘弯屈。我一直安慰自己,他們只是感情好恋拷,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布资厉。 她就那樣靜靜地躺著,像睡著了一般蔬顾。 火紅的嫁衣襯著肌膚如雪宴偿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天诀豁,我揣著相機與錄音窄刘,去河邊找鬼。 笑死舷胜,一個胖子當(dāng)著我的面吹牛娩践,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播烹骨,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼翻伺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沮焕?” 一聲冷哼從身側(cè)響起吨岭,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎峦树,沒想到半個月后辣辫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡魁巩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年急灭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歪赢。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡化戳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情点楼,我是刑警寧澤扫尖,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站掠廓,受9級特大地震影響换怖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蟀瞧,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一沉颂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悦污,春花似錦铸屉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至踏枣,卻和暖如春昌屉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背茵瀑。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工间驮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人马昨。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓竞帽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親偏陪。 傳聞我的和親對象是個殘疾皇子抢呆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354