Loading...

JavaScript操作符

MILLEROS 发布于 2019-04-01

1. 一元操作符

只能操作一个值的操作符叫做一元操作符。

1.1 递增和递减操作符

递增和递减操作符一般是连用两个一元操作符来完成操作。递增和递减操作符又分为前置递增/递减后置递增/递减

1.1.1 前置递增/递减

var age = 22;
console.log(--age); //21

1.1.2 后置递增/递减

var age = 22;
console.log(age--); //21

1.1.3 在运算中运用递增或递减运算符

在实际的等式运算中对变量可以进行任意的一元运算,但是对于实际数值使用运算则可能会出错。

var num1 = 4;
console.log(num1-- + 1); //5
console.log(num1); // 3
console.log(--num1 + 1); //3

console.log(--4 + 1); // Uncaught ReferenceError: Invalid left-hand side expression in prefix operation
console.log(4-- + 1); // Uncaught ReferenceError: Invalid left-hand side expression in prefix operation
console.log(1 + 4--); // Uncaught ReferenceError: Invalid left-hand side expression in prefix operation

这里有两处地方值得我们注意,第一处是在代码的第二行和第四行处,为何前置和后置递减操作分别运算加一操作会得出两个不同的结果。 首先第二行后置递减,此处的运算使用的num1值是num1执行后置递减前的值。 然后第四行前置递减,此处的运算使用的num1值是num1执行第二行后置递减接着再执行第四行前置递减后的值。 第二处值得我们注意的是直接对数值进行递增或递减操作是会报错的。

1.2 运用一元操作符进行数值转换

我们除了可以在等式运算中使用一元运算符之外,还可以在JavaScript中对几乎任何值进行一元操作,不过在对非数值应用一元操作符时,该操作符会像Number()转型函数一样对这个值执行转换。 换句话说,布尔值false和true将被转换为0和1,字符串值会被按照一组特殊的规则进行解析,而对象则是先调用他们的valueOf()和toString()方法,再转换得到的值。

var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = false;
var f = 1.1;
var o = {
    valueOf:function(){
        return -1;
    }
};

s1 = + s1;     //数值1
s2 = + s2;     //数值1.1
s3 = +s3       //NaN
b = +b;        //数值0
f = +f;         //数值1.1
o = +o;        //数值-1

2. 位操作符

位操作符作用在最基本的二进制上,也就是内存中,此处的位指的就是内存中二进制各数值的位置得来的。ECMAScript中的所有数值都是以IEEE-754 64位格式存储,但操作符并不直接操作64位的值,而是先将64位转换成32位的整数,然后再执行操作。

对于有符号的整数,32位中的前31位用于表示整数的值,第32位用于表示数值的符号:0表示正数,1表示负数。这个标识符好的位叫做符号位,符号位的值军订了其他位数值的格式。

正数以纯二进制格式存储,31位中的每一位都表示2的幂。第一位(位0)表示2º,以此类推。32位中表示数值时,没有用到的位使用0填充。例如 18的二进制表示:00000000000000000000000000010010,或者简洁表示为10010。拥有五个有效位五位本身军订了实际的值。

| 1 | 0 | 0 | 1 | 0 | | ------------ | ------------ | ------------ | ------------ | ------------ | | 2^4x1 | 2³x0 | 2²x0 | 2¹x0 | 2ºx0 | | 16 | 0 | 0 | 2 | 0 | | | | | 合计 | 18 |

负数同样以二进制码存储,但使用的格式是二进制补码。计算一个数值的二进制补码,需要三个步骤:

  1. 求这个数值绝对值的二进制码,例如-18,先求18的二进制码。
  2. 求二进制反码,也就是将0替换为1,将1替换为0。
  3. 得到的二进制反码加1。

根据三个步骤求-18的二进制码: 0000 0000 0000 0000 0000 0000 0001 0010 求二进制反码,0与1互换 1111 1111 1111 1111 1111 1111 1110 1101 二进制反码加1 1111 1111 1111 1111 1111 1111 1110 1110

此时便就得到了二进制表示的-18。

但是在我们在JavaScript中计算二进制的时候,负数的二进制并不是如实显示上述的计算结果,例如-18的二进制在JavaScript中显示的是:

var num = -18;
console.log(num.toString(2)); // "-10010",也就是直接在18的二进制码前面加上了负号。
继续阅读

Let’s Encrypt Certbot 报错

MILLEROS 发布于 2019-03-18

关于给网站添加https的必要性在这里就不再赘述了,合理的添加https可以防止很多安全问题,其中就包括恶心的运营商广告注入问题。

现在普遍流行的https提供商非Let’s Encrypt莫属了,根据其提供的Certbot工具,我们可以轻松、优雅的为我们的域名创建Nginx/Apache证书;工具为我们生成的证书有效期限均为90天,所以我们每过90天便需要对我们的证书进行更新。在更新证书的过程中,我们经常会遇到各种各样的问题。

以下是我个人遇到的一些问题汇总,长期更新。

更新域名证书

  1. Client with the currently selected authenticator does not support any combination of challenges that will satisfy the CA.

出现这种错误一般情况下是所使用的客户端未更新或者Nginx/Apache服务未及时停止所致应对措施如下: 更新Certbot:

yum upgrade certbot //使用yum安装的certbot时
apt-get upgrade certbot //使用apt-get安装的certbot时
...

//使用手动安装方法时
wget https://dl.eff.org/certbot-auto //下载wget工具
chmod a+x ./certbot-auto //修改权限
./certbot-auto --help //测试打开certbot帮助

Nginx/Apache服务未及时停止

//Apache
sudo certbot --authenticator standalone --installer apache -d example.com -d www.example.com --pre-hook "systemctl stop apache2" --post-hook "systemctl start apache2"

//Nginx
sudo certbot --authenticator standalone --installer nginx -d example.com -d www.example.com --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx"

关于证书的维护问题

服务器如果缺少日常维护,经常会多出很多无用的问题,例如当前的certbot续期出现问题时,我们执行某些命令更新证书时常常会出现同一域名出现了多个证书,实际有用的只有一个,此时我们就需要删除无用的证书以及对应的Nginx/Apache配置文件。

从证书中删除域名

删除证书的时候,需要删除archive中的文件和live中的符号链接,同时还需要删除证书更新的配置文件:

rm -rf /etc/letsencrypt/live/www.example.com/
rm -rf /etc/letsencrypt/archive/www.example.com/
rm -rf /etc/letsencrypt/renewal/www.example.com.conf

这里要谨慎使用rm -rf命令,因为在输入文件路径时我们可能误按回车键,如果使用了-rf指令啧我们会在不收到任何提示的情况下删除某个文件

删除了旧的证书,我们就不用在每次自动更新证书时选择需要更新的证书,也会减少多种因为Nginx/Apache配置文件与证书文件不对应而产生的问题。

继续阅读

整理的一些前端安全问题

MILLEROS 发布于 2019-02-25

Web security 1.0

主要整理了前端的一些常见的安全问题。

JS逻辑代码

1. XSS攻击

主要表现为注入代码,在客户端渲染数据时,应该无条件相信用户的输入是不可信的,也就是说当渲染用户输入的数据时,应当提前剔除可执行代码或者直接进行转义。 XSS攻击通常是下面的攻击形式:

  • 使用vue等框架渲染时
<div class="content" v-html="data.content"></div> <!-- 🈲直接渲染用户的输入为html -->
<div class="content">{{data.content}}</div> <!-- ✅使用带有转义作用的渲染方式 -->
<script>
    data(){
        return {
            content:"<script>alert('您被XSS攻击了')</script>"
        }
    }
</script>
  • 使用JS、JQ渲染数据时
<div class="content"></div>
<script>
    var _incomeMsg = '<script>alert('您被XSS攻击了')</script>';

    document.getElementByClass('content')[0].innerHtml = _incomeMsg; // 🈲直接渲染用户的输入为html
    document.getElementByClass('content')[0].innerText = _incomeMsg; // ✅使用带有转义作用的渲染方式

    $('.content').append(_incomeMsg);// 🈲直接渲染用户的输入为html
    $('.content').after(_incomeMsg);// 🈲直接渲染用户的输入为html
    $('.content').before(_incomeMsg);// 🈲直接渲染用户的输入为html

    $('.content').text(_incomeMsg);// ✅使用带有转义作用的渲染方式
</script>

<!-- 执行exec等函数 -->
<script>
function html2Escape(sHtml) {
 return sHtml.replace(/[<>&"]/g,function(_index){
   return {'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;'}[_index];
 });
}

var _url = "https://lingtaikj.com?username=<script>alert('您被XSS攻击了')</script>"
var param = /=(.+)$/.exec(_url);
var value = decodeURIComponent(param[1]);

$('.content').append('<a class="appendLink">' + _url + '</a>');// 🈲直接渲染用户的输入为html
$('.content').append('<a class="appendLink">' + html2Escape(_url) + '</a>');// ✅使用函数将html标签转换为实体编码
</script>

2. CSRF攻击

CSFR跨站请求伪造攻击,主要利用不合理的POST或GET请求来达成某种目的。

2.1 GET请求

某些时候,一些后端工程师为了方便,会将某些post请求写成GET请求,殊不知这样是违反HTTP标准的,同时也容易被黑客所利用:

<?php
// 从cookie中获取用户名,看似稳妥
$username = $_COOKIE['username'];
$productId = $_GET['pid'];
// 这里进行购买操作
//store_into_database($username, $productId);
?>
<meta charset="utf-8" />
<?php
echo $username . '买入商品:' . $productId;
?>

此时我们直接使用浏览器访问目标端口即可达成请求

2.2 POST请求

虽然我们此时按照了HTTP标准来编写请求接口,但是此时还是可以破解的。

<?php
$username = $_COOKIE['username'];
// 换为post了,可以规避黑客直接的提交
$productId = $_POST['pid'];
// 这里进行购买操作
//store_into_database($username, $productId);
?>
<meta charset="utf-8" />
<?php
echo $username . '买入商品:' . $productId;
?>

此时在html网页中可以伪造一个表单等待用户点击。

<!DOCYTPE HTML>
<html>
    <head>
        <meta charset="utf-8" />
        <script src="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/jquery-1.10.2_d88366fd.js"></script>
    </head>
    <body>
        <button id="clickme">点我看相册</button>
        <script>
            $('#clickme').on('click', function () {
                // 用户再不知情的情况下,提交了表单,服务器这边也会以为是用户提交过来的。
                $('#myform').submit();
            });
        </script>
        <form id="myform" style="display:none;" target="myformer" method="post" action="http://myhost:8082/lab/xsrflab/submit.php">
            <input type="hidden" name="pid" value="1">
        </form>
        <iframe name="myformer" style="display:none;"></iframe>
    </body>
</html>

2.3 如何防范两种请求潜在的CSRF攻击

除了要遵守HTTP标准来编写接口以外,我们可以在请求时给请求加上验证码,如果验证码填写不对,则请求无效,但是缺点是降低用户体验,用户每次请求都需要输入验证码,这是极其不友好的。

另外一种方法是在POST等敏感请求上加上二次验证,即在每个请求上加上页面token验证,当用户每次访问某个页面时,生成一个token下发给用户,当用户请求时验证token是否有效,这样当黑客在使用CSRF时则不会带有网站生成的请求Token,此连接操作自然是无效的。

3. 网络劫持

3.1 HTTP劫持

很多的时候,我们的网站不是直接就访问到我们的服务器上的,中间会经过很多层代理,如果在某一个环节,数据被中间代理层的劫持者所截获,他们就能获取到使用你网站的用户的密码等保密数据。比如,我们的用户经常会在各种饭馆里面,连一些奇奇怪怪的wifi,如果这个wifi是黑客所建立的热点wifi,那么黑客就可以结果该用户收发的所有数据。这里,建议站长们网站都使用https进行加密。这样,就算网站的数据能被拿到,黑客也无法解开。

如果你的网站还没有进行https加密的化,则在表单提交部分,最好进行非对称加密--即客户端加密,只有服务端能解开。这样中间的劫持者便无法获取加密内容的真实信息了。

3.2 iframe劫持

此种表现为自己的网站被其他网站利用iframe嵌套了,如果我们的网站因为某些原因被第三方网站嵌套了,第三方网站可以将嵌套我们网站的iframe透明度设为0覆盖在当前网页之上,然后利用背景诱导用户点击,那么此时就有可能发生不可预料的风险。

解决方法

3.2.1 防网页被嵌套

为了防止网站被钓鱼,可以使用window.top来防止你的网页被iframe.

//禁止任意形式的被嵌套
if(window != window.top){
    window.top.location.href = correctURL;
}

//同域名的网页可嵌套
if (top.location.host != window.location.host) {
  top.location.href = window.location.href;
}
3.2.2 X-Frame-Options

X-Frame-Options是一个响应头,主要是描述服务器的网页资源的iframe权限。目前的支持度是IE8+

X-Frame-Options: DENY
拒绝任何iframe的嵌套请求

X-Frame-Options: SAMEORIGIN
只允许同源请求,例如网页为 foo.com/123.php,則 foo.com 底下的所有网页可以嵌入此网页,但是 foo.com 以外的网页不能嵌入

X-Frame-Options: ALLOW-FROM http://s3131212.com
只允许指定网页的iframe请求,不过兼容性较差Chrome不支持
3.3.3 CSP页面防护

和X-Frames-Options一样,都需要在服务器端设置好相关的Header. CSP 的作用, 真的是太大了,他能够极大的防止你的页面被XSS攻击,而且可以制定js,css,img等相关资源的origin,防止被恶意注入。不过兼容性不好。

Content-Security-Policy: default-src 'self'
3.2.2 sandbox

主要用于防止自己的网页嵌套他人网站时的安全问题

<iframe sandbox src="..."> ... </iframe>

sandbox还忠实的实现了“Secure By Default”原则,也就是说,如果你只是添加上这个属性而保持属性值为空,那么浏览器将会对iframe实施史上最严厉的调控限制,基本上来讲就是除了允许显示静态资源以外,其他什么都做不了。比如不准提交表单、不准弹窗、不准执行脚本等等,连Origin都会被强制重新分配一个唯一的值,换句话讲就是iframe中的页面访问它自己的服务器都会被算作跨域请求。 另外,sandbox也提供了丰富的配置参数,我们可以进行较为细粒度的控制。一些典型的参数如下:

allow-forms:允许iframe中提交form表单
allow-popups:允许iframe中弹出新的窗口或者标签页(例如,window.open(),showModalDialog(),target=”_blank”等等)
allow-scripts:允许iframe中执行JavaScript
allow-same-origin:允许iframe中的网页开启同源策略

4. 控制台注入代码

通常此种攻击方式较为罕见,主要表现为黑客利用不懂代码的小白,诱骗他们在控制台粘贴执行某些代码,从而达到窃取用户信息的目的。

解决方案 在控制台显著标识警示信息。可参考天猫官网,打开控制台查看安全警告

5. 钓鱼网站

一般钓鱼网站会伪造成和正常网站,此时提示用户输入账号密码,提交之后用户对方便获得了用户的账号密码等信息。

一般是在原网站上发布钓鱼网站的外链,诱导用户点击。

解决办法 在网站内所有不可信的外链均执行页面unload提示,或者将所有的不可信外链改成拦截后需要用户确认为新窗口打开。

Cookie主要是用来维护用户的登录状态的,同时有可能会存储其他敏感信息;当我们网页中的Cookie被他人,例如黑客利用XSS攻击或者其他方式获取了您的Cookie,那么他将可以利用您的Cookie以您的身份登录网站并进行操作。

  • 解决办法 要解决用户的Cookie可能被窃取的问题,通常的做法是后端工程师在处理登录注册操作时将用户的cookie设置为HttpOnly
setcookie("Session", "sessionKey", NULL, NULL, NULL, NULL, TRUE/*httpOnly*/);
继续阅读

简单的Python爬虫爬取网页信息

MILLEROS 发布于 2018-06-25

前言

从一开始使用Nodejs编写爬虫爬去网页信息开始,就对爬虫感兴趣了,以为可以做很多低级但是繁琐的事情,当然,这里的低级是一方面是因为以我现在的阶段不需要爬取太多深层次的东西,另一方面则是爬虫的编写水平还跟不上,当然肯定是后者才是重点hhhh。

爬虫的原理

编写网页爬虫一般是对页面进行解析,现在百度一搜索爬虫,最多的就是爬取豆瓣电影等网站的爬虫代码。

爬虫代码

# encoding=utf-8
import requests
import time
from bs4 import BeautifulSoup

_downLoadUrl = 'https://so.gushiwen.org/mingju/'
_count = 0
_all = 200

def getMs():
    millis = int(round(time.time() * 1000))
    return millis

def downLoadPage(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36'
    }
    data = requests.get(url,headers=headers).content
    return data

def getList(doc):
    soup = BeautifulSoup(doc,'html.parser')
    _list = soup.find('div',class_='sons')
    _pageCount = 0
    for _item in _list.find_all('div',class_='cont'):
        _pageCount += 1
        _detail = '[' + str(_pageCount) + '/' + str(_count) + ']' + _item.get_text()

        # print(_detail)
        file = open("gushiwen.txt","a")
        file.write(_detail)
        file.close()

    # global _all
    # _all = int(soup.find('label',id_='sumPage').get_text()) #获取所有页数

    page = soup.find('a',attrs={"class":"amore", "href": True}) #获取下一页
    if page:
        return _downLoadUrl + page['href'].replace('/mingju/','')
    return None

def main():
    url = _downLoadUrl
    _start = getMs()

    while url:
        global _count
        _count +=1

        doc = downLoadPage(url)
        url = getList(doc)

        print('第' + str(_count) + '页已经爬取完成,还剩' + str(_all - _count) + '页待爬取!')

        if (url == None):
            _end = getMs()
            print('爬取完成,耗时' + str(_end - _start) + '毫秒!')

if __name__ == '__main__':
    main()
继续阅读

Nuxt.js -- Vue.js 单页应用的服务端渲染

MILLEROS 发布于 2018-05-15

简介

Vuejs现在几乎成了单页应用的代名词,但作为单页应用,在搜索引擎的友好程度上显然是不行的,当网站爬虫爬取到vue应用时如果没有做过特殊的处理,爬虫通常是爬取不到页面的主要内容的,因为vue单页应用通常使用的都是前端渲染,而爬虫爬取通常是不执行JS的;所以在种种情况下Vue的服务端渲染显然是作为对SEO优化最好的选择,因此Nuxt.js应运而生。

Nuxt.js

Nuxt.js 是一个基于 Vue.js 的通用应用框架,它预设了利用 Vue.js 开发 服务端渲染(SSR, Server Side Render) 的应用所需要的各种配置,同时也可以一键生成静态站点。

作为框架,Nuxt.js 为 客户端/服务端 这种典型的应用架构模式提供了许多有用的特性,例如异步数据加载、中间件支持、布局支持等。区别于其他 vue SSR 框架,Nuxt.js 有以下比较明显的特性。

  • 自动代码分层
  • 强大的路由功能,支持异步数据(路由无需额外配置)
  • HTML头部标签管理(依赖 vue-meta 实现)
  • 内置 webpack 配置,无需额外配置

起步

项目创建

使用基于vue-cli的脚手架工具

$ vue init nuxt-community/starter-template <project-name>

通过此命令行命令初始化一个nuxt项目。

目录结构

├─assets                      资源目录,未编译的静态资源如less、js
├─components             组件目录,例如页面共用的头部,底部
├─layouts                    布局目录,例如所有页面都分成三部分,上中下
├─middleware             用于存放应用的中间件,允许您定义一个自定义函数运行在一个页面或一组页面渲染之前。
├─node_modules           
├─pages                      页面目录,放置页面除去组件后的主要部分
  ├─index.vue
  ├─....                 
├─plugins                    插件 例如jq,swiper等插件的文件
├─static                      静态文件目录
├─store                       vuex store 

vuex store : vuex状态树

配置项

// nuxt.config.js 文件配置
const path = require('path')

module.exports = {
  // Headers of the page
  head: {
    title: '默认共用title', //对应HTML<title />
    meta: [ //对应HTML<meta>
      { charset: 'utf-8' },
      { 'http-equiv': 'pragma', content: 'no-cache' },
      { 'http-equiv': 'cache-control', content: 'no-cache' },
      { 'http-equiv': 'expires', content: '0' },
      { content: 'telephone=no', name: 'format-detection' }
    ],
    // html head 中创建 script 标签
    script: [
      { innerHTML: require('./assets/js/flexible_nuxt'), type: 'text/javascript', charset: 'utf-8'}
    ],
    // 不对<script>标签中内容做转义处理
    __dangerouslyDisableSanitizers: ['script']
  },
  // Global CSS
  css: ['~/assets/css/reset.css', '~/assets/css/main.less'],
  // Global env
  env: {
    __ENV: process.env.__ENV
  },
  build: {
    vendor: ['axios'],
    postcss: [
      require('postcss-px2rem')({
        remUnit: 75
      })
    ],
    extend (config, ctx) {
      if (ctx.isClient) {
        // 拓展 webpack 配置
        config.entry['polyfill'] = ['babel-polyfill']
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
        // 添加 alias 配置
        Object.assign(config.resolve.alias, {
          'utils': path.resolve(__dirname, 'utils')
        })
      }
    }
  },
  plugins: [{src: '~plugins/toast', ssr: false}, {src: '~plugins/dialog', ssr: false}]
}

头部标签配置

Nuxt.js 通过 vue-meta 实现头部标签管理,在 nuxt.config.js 中的 head 配置。所有的页面都会走这个配置,如果想要修改某一页面的title,可以在 pages/yourpage.vue 文件下,添加如下配置,这时该页面的标题就变成了“This is Miller”,其余页面还保持原有标题不变。

<script>
    export default {
        head () {
            return {
                title:'This is Miller'
            }
        }
    }
    //do something
</script>

在config header配置中, __dangerouslyDisableSanitizers: ['script'] 主要是为了不对innerHTML中内容做转义处理

路由

Nuxt.js 依据 pages 目录结构,自动生成 vue-router 模块的路由配置。

详情可查看Nuxt.js官网路由板块

布局

Nuxt.js布局方式如下图所示: Nuxt.js布局方式

布局配置文件存储在layouts文件夹下,默认布局为default.vue:

<template>
  <div id="milleros">
    <AppHeader />
    <nuxt/>
    <AppFooter />
    <AppLoading />
  </div>
</template>

这里<nuxt/>是一个页面的主体,所有/pages/*.vue中的内容都会插入到此标签中;

<AppHeader />、<AppFooter />、<AppLoading />为conponents组件,是所有页面共用的组件。

所以layouts布局就是配置不同组件和页面主体的结构。

我们可以根据不同的页面定制不同的layouts布局:

<script>
export default {
  layout: 'demo_layout',
  //do something
}
</script>

vuex

在根目录创建 store 目录,就会默认引用 vuex 模块,除此之外,还进行了以下的操作: 1)将 vuex 模块 加到 vendors 构建配置中去; 2)设置 Vue 根实例的 store 配置项。

Nuxt.js 支持两种使用 store 的方式:

  • 普通方式:store/index.js 返回一个 Vuex.Store 实例
  • 模块方式:store 目录下的每个 .js 文件会被转换成为状态树指定命名的子模块 (当然,index 是根模块,相当于设置了namespaced: true)

Nuxt.js提供了模块方式的简单写法:使用状态树模块化的方式,store/index.js 不需要返回 Vuex.Store 实例,直接将 state、mutations 和 actions 暴露出来即可。示例如下:

export const state = () => ({
  accesstoken: ''
})

export const mutations = {
  setAccesstoken (state, accesstoken) {
    state.accesstoken = accesstoken
  }
}

asyncData

asyncData方法是服务端渲染的重点!!!

因为asyncData可以在组件(仅限于页面组件)每次加载之前被调用。而且可以再服务端或路由调用之前被调用

Nuxt.js 会将 asyncData 返回的数据融合组件 data 方法返回的数据一并返回给当前组件。

详细查看异步数据
继续阅读

记一次服务器文件被恶意篡改的解决过程

MILLEROS 发布于 2018-05-02

前言

本人也是个linux小白,下面的内容是记录一次服务器找出源文件被篡改的原因的过程。

最近遇到公司服务器文件被篡改的情况,主要表现就是公司官网主页的SEO信息被替换成了广告,一开始以为只是普通的劫持,但是后来上到服务器发现是源文件直接被篡改了,然后找了一圈服务器登录记录以及命令执行记录都没发现有可以目标存在,后来又怀疑是设置了定时任务,然而也并没有发现。在没有明确发现可以目标的情况下,而且下午就放五一小长假了就没去深究,直接把文件改回来了。

刚放完假的第一天回来发现文件再次被篡改,篡改时间为5月1日下午两点十几分,这就让我有点头疼了,但是一圈检查下来还是没有发现任何记录,怀疑是被删掉了或者干脆就是自动某个自动脚本修改的,因为修改之后的源文件编码不对了,原先是utf-8,现在直接就是GBK了。

本来想查看文件修改记录的,但是发现服务器上并没有发现任何监控程序启动,所以只能将文件再次改回去,然后使用最基础的auditd官网来监控整个被篡改过文件的目录。

auditd

使用auditd监控文件变化记录。

到了这里我们其实已经可以先设定一个设计规则了,然后等待上述说到的文件夹被篡改后查看审计报告后在做计划了。

等待

等待所监控的文件再次被修改

文件再次被篡改

服务器首页文件在修正后并且添加了监控的情况下再次被篡改,使用命令

$ sudo ausearch -f /var/www/html

并未发现存在文件修改记录,猜测文件并非从服务器端直接被修改,且注入的文字为HTML实体编码,修改后的文件编码被改变。

猜测为注入攻击

查看网站访问记录

发现可疑访问

查看apache日志发现每次文件被篡改都显示有http://www.link114.cn/ 的访问

whois反查

通过whois反查查到以下注册人信息

  • 名称:陈镇威
  • 邮箱:520vigo@163.com
  • 域名列表:hackerjs.cn、link114.cn、link114.com.cn、netloss.cn、showdownload.org

其中link114.cn为买链帮手,判断是此人所为

查到个人博客

通过google查到此邮箱所关联的网易博客,但最近的博客更新时间为2008年,无法找到更多有用信息

返回梳理发生过程

通过对文件从修改正确到再次被篡改的过程中梳理得出了我从一开始就忽略的问题——文件夹权限问题

通过ls查看/var/www/文件夹权限为777,其下所有文件夹几乎均为权限777,这就会导致一个问题,那就是任何人都可以使用工具向我们的服务器内HTTP PUT文件,而且脚本为最高权限。

调整文件、文件夹权限

/var/www/下的所有文件/文件夹所属用户和组修改为apache,修改权限为755

后来测试发现项目上传文件夹无法上传,将权限修改为drwxrwx---后可以上传了。

总结

通过这次的教训,深刻认识到了服务器文件夹权限的重要性,往严重了说,这次对方修改meta信息算是轻得了,严重了都可以造成整个服务器被格盘或者干脆被当成肉机还不知情。

继续阅读

使用auditd监控文件/文件夹修改记录

MILLEROS 发布于 2018-05-02

什么是auditd

auditd(或 auditd 守护进程)是Linux审计系统中用户空间的一个组件,其负责将审计记录写入磁盘。说白了就是开启的一个守护进程根据指定的规则来监控系统的某些情况变化。

安装audited

我当前使用的操作系统是Centos 6,发现audited默认安装了,要查看audited是否安装了只需输入以下代码

$ sudo auditctl -v

显示了版本信息说明已安装

auditctl version 2.4.5

否则就使用apt-get或者yum、rpm等包管理工具安装

$ sudo yum install auditd

安装完毕后默认以下的工具也会存在

  • auditctl : 即时控制审计守护进程的行为的工具,比如如添加规则等等。
  • /etc/audit/audit.rules : 记录审计规则的文件。
  • aureport : 查看和生成审计报告的工具。
  • ausearch : 查找审计事件的工具
  • auditspd : 转发事件通知给其他应用程序,而不是写入到审计日志文件中。
  • autrace : 一个用于跟踪进程的命令。
  • /etc/audit/auditd.conf : auditd工具的配置文件。

安装完后记得查看auditd是否启动

$ sudo service auditd status

否则使用以下命令启动

$ sudo service auditd start

使用以下命令停止

$ sudo service auditd stop

首次安装 auditd 后, 审计规则是空的:

$ sudo auditctl -l
No rules

如何使用auditd

Audit 文件和目录访问审计

我们使用审计工具的一个基本的需求是监控文件和目录的更改。使用auditd工具,我们可通过如下命令来配置(注意,以下命令需要root权限)。

文件审计
$ sudo auditctl -w /var/www/html/index.html -p rwxa

选项 :

  • -w path : 指定要监控的路径,上面的命令指定了监控的文件路径 /etc/passwd,必须是以‘/’开头的绝对路径
  • -p : 指定触发审计的文件/目录的访问权限
  • rwxa : 指定的触发条件,r 读取权限,w 写入权限,x 执行权限,a 属性(attr)
目录审计

使用类似的命令来对目录进行审计,如下:

$ sudo auditctl -w /var/www/html/

以上命令将监控对 /var/www/html/ 目录 的所有访问。 现在,可以运行 auditctl -l 命令即可查看所有已配置的规则。

$ auditctl -l
-w /var/www/html/index.html -p rwxa
-w /var/www/html/ -p rwxa

查看审计日志

添加规则后,我们可以查看 auditd 的日志。使用 ausearch 工具可以查看auditd日志。

我们已经添加规则监控 /var/www/html/ 文件。现在可以使用 ausearch 工具的以下命令来查看审计日志了。

$ sudo ausearch -f /var/www/html/
----
time->Wed May  2 11:20:31 2018
type=PATH msg=audit(1525231231.453:528): item=1 name="/var/www/html/.index.html.swp" inode=929483 dev=fc:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=DELETE
type=PATH msg=audit(1525231231.453:528): item=0 name="/var/www/html/" inode=921826 dev=fc:01 mode=040777 ouid=0 ogid=0 rdev=00:00 nametype=PARENT
type=CWD msg=audit(1525231231.453:528):  cwd="/var/www/html/"
type=SYSCALL msg=audit(1525231231.453:528): arch=c000003e syscall=87 success=yes exit=0 a0=ea54a0 a1=1 a2=1 a3=0 items=2 ppid=15009 pid=15413 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts4 ses=54129 comm="vim" exe="/usr/bin/vim" key=(null)

配置项:

  • -f 设定ausearch 调出 /var/www/html

解读输出结果:

  • time : 审计时间
  • name : 审计对象
  • cwd : 当前路径
  • syscall : 相关的系统调用
  • auid : 审计用户ID
  • uid 和 gid : 访问文件的用户ID和用户组ID
  • comm : 用户访问文件的命令
  • exe : 上面命令的可执行文件路径

以上审计日志显示文件曾被vim(/usr/bin/vim)改动过。

查看审计报告

一旦定义审计规则后,它会自动运行。过一段时间后,我们可以看看auditd是如何帮我们跟踪审计的。

Auditd提供了另一个工具叫 aureport 。从名字上可以猜到, aureport 是使用系统审计日志生成简要报告的工具。

我们已经配置auditd去跟踪/var/www/html。auditd参数设置后一段时间后,audit.log 文件就创建出来了。

生成审计报告,我们可以使用aureport工具。不带参数运行的话,可以生成审计活动的概述。

$ sudo aureport

Summary Report
======================
Range of time in logs: 05/02/2018 11:18:36.200 - 05/02/2018 12:31:43.069
Selected time for report: 05/02/2018 11:18:36 - 05/02/2018 12:31:43.069
Number of changes in configuration: 4
Number of changes to accounts, groups, or roles: 0
Number of logins: 2
Number of failed logins: 1
Number of authentications: 4
Number of failed authentications: 6
Number of users: 1
Number of terminals: 8
Number of host names: 3
Number of executables: 4
Number of commands: 2
Number of files: 123
Number of AVC's: 0
Number of MAC events: 0
Number of failed syscalls: 790
Number of anomaly events: 0
Number of responses to anomaly events: 0
Number of crypto events: 33
Number of integrity events: 0
Number of virt events: 0
Number of keys: 0
Number of process IDs: 176
Number of events: 1878

如上,报告包含了大多数重要区域的信息。

上图可以看出有 1 次授权失败。 使用aureport,我们可以深入查看这些信息。

使用以下命令查看授权失败的详细信息:

$ sudo aureport -au

Authentication Report
============================================
# date time acct host term exe success event
============================================
1. 05/02/2018 11:42:11 root 14.*.79.* ssh /usr/sbin/sshd no 1161
2. 05/02/2018 11:42:17 root 14.*.79.* ssh /usr/sbin/sshd no 1162
3. 05/02/2018 11:42:17 root 14.*.79.* ssh /usr/sbin/sshd no 1163
4. 05/02/2018 11:42:22 root 14.*.79.* ssh /usr/sbin/sshd yes 1164
5. 05/02/2018 11:42:22 root 14.*.79.* ssh /usr/sbin/sshd yes 1167
6. 05/02/2018 12:31:30 root 14.*.79.* ssh /usr/sbin/sshd no 1866
7. 05/02/2018 12:31:38 root 14.*.79.* ssh /usr/sbin/sshd no 1867
8. 05/02/2018 12:31:38 root 14.*.79.* ssh /usr/sbin/sshd no 1868
9. 05/02/2018 12:31:42 root 14.*.79.* ssh /usr/sbin/sshd yes 1869
10. 05/02/2018 12:31:42 root 14.*.79.* ssh /usr/sbin/sshd yes 1872

从上图可以看出,由一个用户在特定的时间(从上一次登录成功的时间开始计算)授权失败。

如果我们想看所有账户修改相关的事件,可以使用-m参数。

$ sudo aureport -m

Account Modifications Report
=================================================
# date time auid addr term exe acct success event
=================================================
<no events of interest were found>

我这里因为刚开启auditd服务所以并未有记录存在。

Auditd 配置文件

我们已经添加如下规则:

  • $ sudo auditctl -w /var/www/html/index.html -p rwxa
  • $ sudo auditctl -w /var/www/html/

现在,如果确信这些规则可以正常工作,我们可以将其添加到 /etc/audit/audit.rules 中使得规则永久有效。以下介绍如何将他们添加到/etc/audit/audit.rules中去。

$ sudo vim /etc/audit/audit.rules

# This file contains the auditctl rules that are loaded
# whenever the audit daemon is started via the initscripts.
# The rules are simply the parameters that would be passed
# to auditctl.

# First rule - delete all
-D

# Increase the buffers to survive stress events.
# Make this bigger for busy systems
-b 320

# Feel free to add below this line. See auditctl man page

-w /var/www/html/index.html -p rwxa
-w /var/www/html/

编辑完后保存,然后重启auditd守护进程

$ sudo service auditd restart

参考文档:

继续阅读

linux NAT端口转发实现nodejs网站https访问

MILLEROS 发布于 2018-04-30

什么是certbot?

在开始这个教程前我们必须要了解的就是什么是certbot。

certbot是Let's Encrypt为了用户方便生成https证书而开发的自动化程序,通过certbot我们可以生成四个文件分别用于为https加密所用。

安装certbot

首先最保险的方法是你需要上Certbot的官网来选择您的服务器对应的操作系统版本

cerbot

我这里使用的语言是nodejs,操作系统是CentOS7,接着下面会出现教程教你如何安装certbot:

certbot安装方法

这里官网提供的方法是使用yum来安装Certbot

$ sudo yum install certbot

但是在CentOS7上,使用yum安装的certbot很可能会出现所安装的certbot和pyOpenSSL版本不对的情况(什么都别说,我就遇到了,可是把我折腾苦了),那么什么是版本不对呢?出现以下错误就说明你的yum安装的pyOpenSSL不对:

Traceback (most recent call last):
  File "/usr/bin/certbot", line 9, in <module>
    load_entry_point('certbot==0.13.0', 'console_scripts', 'certbot')()
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 378, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 2566, in load_entry_point
    return ep.load()
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 2260, in load    entry = __import__(self.module_name, globals(),globals(), ['__name__'])
  File "/usr/lib/python2.7/site-packages/certbot/main.py", line 17, in <module>
    from certbot import client  File "/usr/lib/python2.7/site-packages/certbot/client.py", line 10, in <module>
    from acme import client as acme_client
  File "/usr/lib/python2.7/site-packages/acme/client.py", line 31, in <module>
    requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3()  # type: ignore  File "/usr/lib/python2.7/site-packages/requests/packages/urllib3/contrib/pyopenssl.py", line 112, in inject_into_urllib3
    _validate_dependencies_met()
  File "/usr/lib/python2.7/site-packages/requests/packages/urllib3/contrib/pyopenssl.py", line 147, in _v
alidate_dependencies_met
    raise ImportError("'pyOpenSSL' module missing required functionality. "ImportError: 'pyOpenSSL' module missing required functionality. Try upgrading to v0.14 or newer.

使用pip安装绕过yum安装的版本不对问题

错误提示使用v0.14或者更新版本,先看看pip中的版本:

$ pip search certbot

显示版本为0.14.0;

卸载yum安装的certbot和pyOpenSSL:

$ yum remove certbot pyOpenSSL

升级pip(如果不是最新版本,pip会提示你升级后再使用):

$ pip install –upgrade pip
pip安装certbot: pip install certbot。 碰到了Python.h或者pyconfig.h找不到的错误,于是安装Python的devel包: yum install -y python-devel;再次运行命令,提示找不到opensslv.h头文件,于是安装OpenSSL的devel包: yum install -y openssl-devel;再次运行pip install certbot命令,成功安装certbot;pip安装certbot: pip install certbot。 碰到了Python.h或者pyconfig.h找不到的错误,于是安装Python的devel包: yum install -y python-devel;再次运行命令,提示找不到opensslv.h头文件,于是安装OpenSSL的devel包: yum install -y openssl-devel;再次运行pip install certbot命令,成功安装certbot;

测试certbot是否可用: certbot certificates。输出正常,说明pip安装了最新版的certbot,并且能正确运行。

生成https证书

前面安装完certbot之后就到了最重要的地方了,就是生成https证书。

首先运行下列命令

$ certbot certonly

会出现两种选择,我们选择第二种

How would you like to authenticate with the ACME CA?
-------------------------------------------------------------------------------
1: Spin up a temporary webserver (standalone)
2: Place files in webroot directory (webroot)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

接着这里我们需要输入自己的域名:www.mydomain.com (注意这里有一个空格) mydomain.com,我们这里绑定的是两个域名,一个是带www的,一个是没有www的,如果你只需要其中一个的话这里可以只写一个

Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'to cancel):

这里要求我们输入网站的根目录

Select the webroot for milleros.com:
-------------------------------------------------------------------------------
1: Enter a new webroot
-------------------------------------------------------------------------------
Press 1 [enter] to confirm the selection (press 'c' to cancel):

如果前面输入域名的时候您输入的是两个则这里会再次提示输入网站根目录:

Select the webroot for yourdomain:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /root/www/static(你前面输入过的网站根目录)
-------------------------------------------------------------------------------

如果根目录一致的话我们就选择2.

输入完根目录之后certbot会自动为我们生成证书,生成成功会有以下提示:

Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/xxx.ml/fullchain.pem. Your cert will
   expire on 2017-06-07. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot again. To
   non-interactively renew *all* of your certificates, run "certbot
   renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
注意:这里我遇到过一个很坑爹的问题,就是输入了网站根目录之后certbot提示我生成证书时认证失败:
Failed authorization procedure. example.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://example.com/.well-known/acme-ch    <head><title>404 Not Found</title></head>
    <body bgcolor="white">    <center><h1>404 Not Found</h1></center>    <hr><center>", www.example.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://www.example.com/.well-known/acme-challenge/k
    <head><title>404 Not Found</title></head>
    <body bgcolor="white">    <center><h1>404 Not Found</h1></center>    <hr><center>"

    IMPORTANT NOTES:
     - If you lose your account credentials, you can recover through       e-mails sent to example@example.com.
     - The following errors were reported by the server:

       Domain: example.com
       Type:   unauthorized
       Detail: Invalid response from
       http://example.com/.well-known/acme-challenge/wGNv57IGJjHQ9wyzzALktpNaPzfnTtN3m7u3QuO4p40:       "<html>
       <head><title>404 Not Found</title></head>
       <body bgcolor="white">       <center><h1>404 Not Found</h1></center>       <hr><center>"

       Domain: www.example.com
       Type:   unauthorized
       Detail: Invalid response from
       http://www.example.com/.well-known/acme-challenge/kFJ0CSuKOdgcT2xmciB4GGNCcnUPoIbpQmA9jOII_Bk:       "<html>
       <head><title>404 Not Found</title></head>
       <body bgcolor="white">       <center><h1>404 Not Found</h1></center>       <hr><center>"

       To fix these errors, please make sure that your domain name was
       entered correctly and the DNS A record(s) for that domain
       contain(s) the right IP address.
     - Your account credentials have been saved in your Certbot
       configuration directory at /etc/letsencrypt. You should make a
       secure backup of this folder now. This configuration directory will
       also contain certificates and private keys obtained by Certbot so
       making regular backups of this folder is ideal.

这里我研究了很久,网上很多人说可能是网站根目录下生成的文件夹.well-known 权限不够,但是我去根目录下看了,连那个文件夹都没看到,所以确定肯定不是权限问题,于是我在分析了一下错误信息里面包含404 not found的错误信息,可能是路由的问题,于是我在重复certbot生成https证书的过程中到了输入根目录的时候在结尾对加了一级/public(我用来存放客户端代码的文件夹),发现果然可以用了,顺利生成了https证书;

创建https服务器

经过刚刚的certbot操作,我们已经生成了如下四个文件:

  • cert.pem
  • chain.pem
  • fullchain.pem
  • privkey.pem

首先我们需要在原先的http服务器上做如下改动:

var express = require('express');
var fs = require('fs');
var http = require('http');
var https = require('https');

//引入刚刚生成的https文件
var privateKey  = fs.readFileSync('/path/to/privkey.pem', 'utf-8');
var certificate = fs.readFileSync('/path/to/cert.pem', 'utf-8');
var ca = fs.readFileSync('/path/to/fullchain.pem', 'utf-8');
var credentials = {key: privateKey, cert: certificate ,ca:ca};

//实例化
expressvar app = express();

//创建http以及https服务
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);

//监听端口
httpServer.listen(8321);
console.log('http server start on port 8321');
httpsServer.listen(8322);
console.log('http server start on port 8322');
//默认的http协议端口是80端口,https端口是443端口。

大功告成 然后我们可以直接使用app来进行各种路由操作,现在就大功告成啦:

只有https访问

如果您不想要http访问,只想要https访问,只需要修改我们之前的两条规则:

//var httpServer = http.createServer(app); //之前的创建http服务器
var httpServer = http.createServer(function(req,res){
    res.writeHead(301, {'Location': 'https://milleros.com/'});
    res.end();
}); //将http请求重定向至https
var httpsServer = https.createServer(credentials, app);

上面这段代码的重定向存在一点问题,就是当我们访问的地址不是主页时,而且请求http时则会跳转回https的主页,这显然不是我们想要的结果,所以我们把原先的代码改成这样

var express = require('express');
var fs = require('fs');
var http = require('http');
var https = require('https');

//引入刚刚生成的https文件
var privateKey  = fs.readFileSync('/path/to/privkey.pem', 'utf-8');
var certificate = fs.readFileSync('/path/to/cert.pem', 'utf-8');
var ca = fs.readFileSync('/path/to/fullchain.pem', 'utf-8');
var credentials = {key: privateKey, cert: certificate ,ca:ca};

//实例化
expressvar app = express();

//创建http以及https服务
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);

//监听端口
httpServer.listen(8321);
console.log('http server start on port 8321');
httpsServer.listen(8322);
console.log('http server start on port 8322');
//默认的http协议端口是80端口,https端口是443端口。

//新增一个路由来拦截请求
app.use(function(req,res,next){
    console.log(req.protocol);
    if(req.protocol != "https"){
    res.writeHead(301, {'Location': 'https://milleros.com' + req.path});
    res.end();
    }
    next();//保证请求不被阻塞
});

这样就可以保证,网站中的每一个http请求都可以被重定向至https了。

结语

linux是一个很复杂的操作系统(至少我这么认为),在我们平时进行的操作中经常会遇到各种各样的问题,有些问题在网络上我们可以很轻易的找到解决问题的方法,但是往往很多时候我们遇到的问题是很特殊的,网上几乎很难找到解决的办法,这个时候我们不应该着急,而应该静下心来仔细捋一捋思路,找找可能会导致问题出现的地方。

正是因为linux的复杂性,很多时候我们误操作会导致很多不可预料的问题,像这次我就是因为pyOpenSSL版本不对的问题在寻求很多方法未果之后选择了对系统自带的Python进行升级,正是因为我这一疯狂的想法从而让我误删除了site-packages,这一误操作直接导致的就是yum和pip无法使用,从而让我不得不重置了整个linux系统。

虽然对linux的误操作很容易造成不可估量的错误,但我还是鼓励大家大胆尝试,毕竟只有不停地跌倒才会有更多爬起来的经验,才能让我们更加的强大!

继续阅读

Ueditor + HighLightJs实现代码块高亮

MILLEROS 发布于 2018-04-30

百度Ueditor

Ueditor是百度的一款富文本编辑器,目前在国内大多数网站都会使用到它,而今天我们就来看看,像我们这种三句不离代码的码农对于代码高亮来说是再基本不过的功能了,那么何为代码高亮呢?

普通代码显示 举例一段html代码

<!DOCTYPE html>

<html>

    <head>

        <meta charset="UTF-8">

        <title>Milleros的官方网站</title>

    </head>

    <body>

        <p>欢迎来到milleros的官方网站</p>

    </body>

</html>



这是一段普通的未加任何修饰的html代码段,像这样的代码存在于网页上,我相信如果是我,我连看的勇气都没有。

高亮代码显示 还是上面那段代码,但是现在加上高亮功能

<!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>Milleros的官方网站</title>
        </head>
        <body>
            <p>欢迎来到milleros的官方网站</p>
        </body>
    </html>

可以看到上面的代码的可读性变高了许多。

如何使用 使用方法很简单,当我们使用Ueditor编辑文章时可以在插入代码时点击Ueditor工具栏上的

snipaste_20170528_123657.png

来选择对应的语言,如果工具栏没有这个选项的话就说明您的编辑器没有默认配置这个功能,这个时候您需要对Ueditor的配置文件ueditor.config.js进行相应功能的配置。选择对应语言之后Ueditor编辑器内会出现专门用于放置代码块的pre标签

snipaste_20170528_124333.png



接着需要在我们需要渲染代码文章的页面引入两个文件

样式文件:

<link rel="stylesheet" type="text/css" href="/ueditor/third-party/SyntaxHighlighter/shCoreDefault.css">

脚本文件:

<script type="text/javascript" src="/ueditor/third-party/SyntaxHighlighter/shCore.js"></script>

激活高亮:

当上述文件均被引入之后,需要在页面js内写入下面的代码来执行代码高亮

SyntaxHighlighter.all();
            提示:代码高亮的执行需要在DOM加载完之后再执行,也就是需要执行代码高亮的代码已经被渲染到页面上之后再执行代码高亮。

代码高亮主题 官方主题分类 对于这种视觉上优化的功能,没有主题切换肯定是不“人道”的,百度Ueditor使用的代码高亮工具是第三方的SyntaxHighlighter高亮工具,从它的官网可以看到官方提供了自己的主题

    snipaste_20170528_124928.png

官方主题使用
    官方对于主题的应用,显然也考虑到了简单易用的原则;对于上述的主题样式我们可以使用
<link type="text/css" rel="Stylesheet" href="PATH/Styles/shThemeDjango.css"/>
    来更换对应的主题文件。

主题文件
    对于我们使用的Ueditor,因为是使用第三方库的原因,可能会存在主题文件不完整的问题存在,像楼主使用的是nodejs版的Ueditor,而内置的主题文件只有默认样式,所以我们需要把缺失的主题文件放到对应的文件夹内。具体的位置需要大家自己去看下 Ueditor的目录结构。
继续阅读

有些人25岁就死了,75岁才埋下

MILLEROS 发布于 2018-04-30

在很小很小的时候,我并不懂什么是理想,什么是梦想。

  • “你的理想是什么”

  • “我长大后想当警察,为民除害“

相信这个情景在我们是孩童时代经常会遇到的,那时候的我们总是带着小小的天真坚定说出自己可能并不了解的理想。

初中,依稀记得每次写到有关理想的作文,我总是写着自己要当警察的理想,但是随着我每次对梦想的探讨,都会让我加深对自己理想的动摇,因为我发现,每一次写一篇文章下来,总是绞劲脑汁都无法深入到这个理想的精髓。

高中是我动摇最大的时代,那三年让我彻底的摒弃了自己坚持了从小学到初中结束的理想,因为此刻的我发现,理想就是理想,是理想的生活,是理想中的理想。

我的大学南门有一排写字间,是学校划分给即将毕业的学生创业孵化的摇篮。记得我第一次来到这里的时候是得知自己被录取后,第二天就和妈妈来到了学校,那时候第一眼见到这一排的创业公司,觉得特别吃惊,因为在这时候的我看来,这些把自己的梦想亲自塑造出来的人该是多么的厉害。

某一个晚上,和舍友的闲聊

  • “我刚刚去创意园了“

  • “你去创意园干啥“

  • “我有一个师兄在那边开了自己的公司,我就过去坐一坐了“

  • “那你师兄是做啥的“

  • “他开的是买衣服的“

  • “卖衣服!?“

  • “对,实体店加淘宝店,他以前是玩乐队的,他说以前深职的乐队特别多,玩摇滚的也很多,听他说当时还专门出了一个深职的专辑,不过没有发行,他卖的衣服都是自己设计的“

昨天晚上

  • “那位师兄不做了,他打算自己去找工作了”;

我当时很吃惊(还是这个词),吃惊之后就是满满的无奈。

每个人活着都有自己想做的事,可能对于我来说,大学就是提供给了我最好的时机做自己最喜欢的事,当然对于大多数的大学生来说同样也是如此,但是每每在我们努力朝着自己的方向前进的时候,深深的无力感总会压迫我们脆弱得一塌糊涂的坚持,也许某一天就会垮下去,接受社会的冲刷,让我们认清楚,不管理想有多么的伟大,最后终究会沦为生活的玩偶。这个时候我们或许是25岁了吧…


有些人25岁就死了,75岁才埋下。

继续阅读

我终于知道我为什么喜欢翻唱了

MILLEROS 发布于 2018-04-30

8月2号到8月10号,仔细一想已经过去了八天了。

在过去的八天里,努力的想回到曾经的生活,做任何自己曾经想做的事。

每天9:20准时到达公司,花一分钟打开电脑打开网易云音乐,打开私人FM,又开始了与网易云一整天的日子。偶然看到一个歌评:无法想象一个人在网易云上听的歌超过8000首是有多么的孤独;我心里一惊,立马看了一下自己听过的曲目数。

-“哈,还好只有4291首”

嗯,再打开听歌排行,映入眼帘,一周内最高播放量的是哪吒乐队的闹海;所有时间排行内Love yourself依然是最高的那个。

这几天一直在收集各种翻唱歌曲,就像此刻的心情,既然无法回到原来的日子,那就干脆寻找有以前生活的影子的生活呗。

世界上痛苦的事情莫过于明明很不开心却依然要坚持微笑。

为什么会喜欢翻唱?

对于翻唱的歌曲,一般都会加入自己的改编,而这种改变似乎更能唤起处在某个特殊时期的人的情感,注意这里说的是“处在某个特殊时期”,原版可能是对于大部分人都是很有感染力的,但是可能不会像翻唱那般深的情感。所以现在很多的翻唱都是比较小众化的,直接反映出来的情况就是翻唱的乐评一般都会比原唱少很多;当然这里我也不会否认某些翻唱会比原版还更加火。

当然喜欢翻唱可能也不仅仅是因为翻唱更能够表现自己的心情,也有可能是对比于原唱,翻唱的风格更加不一样了,也就是常提到的“新鲜感”。

最近还看到一句很扎心得乐评:

-“当私人FM推荐的歌都是乐评少于999的时候说明网易云开始和你玩真的了”

这句话的意思可能就是说:

   -“现在没人真正明白你了,也就只有我了”

今天真是瞎比比了一堆没用的废话,总之就是想说,任何时候都只能依靠自己吧,需要在未来的日子里继续加油~

最后再推荐一首扎心得歌:

其实说了半天,也就是只有这里才能让我心里有点归属,说自己想说的,记录下来,未来的日子还长,谁不比谁高贵,扎心也好,无痛呻吟也罢,一切的一切都是自己的心里所想,做自己想做的,顺其自然,一切都是开心就好哈哈哈~~~

继续阅读

其实我也喜欢哆啦A梦

MILLEROS 发布于 2018-04-30

其实写这篇文章最初的想法是源于一个人对于哆啦A梦的喜爱,所以正好唤醒了那个念头吧。

  我们喜欢过的东西太多了,可能喜欢哆啦A梦只是你所有爱好中的其中一个吧。



“不过怎样都是经典我都会接受”

你是觉得幼稚吗?

   那是15年的高考季,在没有任何的期待中我看到了关于《哆啦A梦:伴我同行》在大陆上映的消息,因为从小就比较喜欢蓝胖子的缘故,我毫不犹豫的和身边的朋友提议在周日下午去电影院看。



   ——“不会吧,这些不是小孩子看的吗?”



   那天的我一直沉浸在对这句话的思考中。是啊,可能我就是一个成熟外表装着年幼的心的人吧。

   从那时候开始我就再也不敢和人说我喜欢哆啦A梦了。

我觉得我又有了新的喜好

几年之后我上了大学,从小就被人灌输大学很自由的思想,于是很多藏在心里的喜好都被勇敢的表现出来了,当时正是因为我喜欢Taylor Swift这件事,让我和他渐渐熟络了起来。

他是当时我们班我认为和我最像的一个人,在那个下午我们两个就一直坐在电脑面前,互相分享自己对于自己喜欢的歌手的热爱。在那个过程中他重新定义了我心中对于热爱的定义,他对于欧美歌手的了解远远胜过于我,而正是那时我才开始接触摇滚。

——“我才发现中国摇滚很牛逼”

   后来一段时间他和我说了上面一句话,我觉得很奇怪,一向喜欢欧美摇滚极少关注中国摇滚的他居然开始将喜爱转向中国摇滚了。



   ——“下个月XX号有个音乐节,里面有很多中国摇滚的乐队,你要去吗?”



   那时候的我对于摇滚没有多大的兴趣,所以就没有和他去。



   在后来的一段时间里他经常去各种音乐节现场,而在那段时间里我开始迷上了前端技术,说的这么好听其实就是打代码的。也就在那段时间里我开始朝着自己喜欢的而且觉得对自己有用的方向发展,所以我变得和他越来越不同了。

你以为就你喜欢的东西有用?

——“其实我觉得你经常去那种音乐节现场听摇滚并没有什么用,以后又不能帮助你找工作。”

   在一个平常的午后我对他说了上面那句话。



   我承认这是我说过的最傻逼的一句话。

   在后面很长一段时间内我都会想起当时他平静的表情,从不敢揣摩他当时的想法。

喜欢一样东西有错吗?

   我特别喜欢用网易云边听歌边看下面的评论,因为好像只有这里才能找到和我有一样爱好的人。

我这个人真的是像大雄一样,又冒失又迟钝还不爱学习,又弱势又懒惰还犹豫不决,运动差劲还胆小又健忘,毫不可靠又怕麻烦 一点主见都没有学东西又慢 ,老好人,一得意就忘形,就会撒娇 啊咧!这可怎么办,真是头疼 。而且我没有多啦A梦。那么,就靠自己吧。(来自网易云音乐《ひまわりの約束》222fool的评论)

其实今天的目的就是为了告诉大家,喜欢就是喜欢,不要因为别人的不理解而不愉快,每个人都有自己的想法,因为你喜欢的东西只有你懂得它给你带来的那种心悸。

继续阅读

你认为他真的优秀吗?

MILLEROS 发布于 2018-04-29

就这样 随便吧

嘲笑我们的孤独吧

我们生来不属于什么地方

前言 我一直在想自己是否优秀,这不是自恋,我对自己成长的每一次回顾不能说都很彻底,但是也总是能让我自己得到很多收获。当然今天我们要讨论的是“你认为他真的优秀吗?”,这个题目看起来似乎有点不正经,有点背后说别人短处的嫌疑,但是我要告诉你,今天讨论的话题虽然和TA有关,但是方向却不是TA,而是自己。

那是我的一个算是比较熟的朋友吧,还记得当初认识他的时候是因为部门活动。

   -“你真厉害,好羡慕你们能通过自己的手画出自己的想法的人”

   -“哈哈,我只不过是一个刚刚入门的新手而已,哪里厉害了”

而后来我又得知他在学院的某个比赛上那个第二名,更是觉得这是一个让我学习的楷模,在那一段时间里我总希望能通过多与他接触来提升自己,虽然我根本不知道能学习到什么,但就是抱着这种没有方向的念想,也许我就是被他人的优秀蒙蔽了双眼吧,又或者是我太急于优秀的念想让我没有看清努力的方向。

而在后来的一段时间内接触,发现他其实真的就是很普通的人,而且每次在他被人夸赞的时候已经会有意无意的绕过这个问题了,我知道这个时候这已经要成为他的烦恼了。

是优秀的烦恼还是强加优秀的烦恼? 可能就是当初首次见面的印象太好,才会让我给他强加上了优秀的称号,而这个强加的称号已经渐渐让他变得有些反感了;

在后来很长一段时间内我都没有再说过他优秀了。

-“你觉得你自己真的优秀吗?“

-“优不优秀取决于你对于优秀的定义“

你觉得他真的优秀吗? 大概是我就是生活在一个任何人都比我强的环境里面吧,我从小到大都习惯了平庸,对于同伴对他人的称赞从来都是不绝于耳的。

   -“好厉害啊,连这个你都会”

   -“还好啦,就是平时接触到的比较多”


   -“和你比起来我觉得自己特别普通”

平庸并没有什么错 不知什么时候,我也被某些人冠上了优秀的称号,起初的我觉得这些优秀的称号根本就是无稽之谈,我觉得我有太多的东西需要学习了。但是随着时间的推移,我似乎已经有点无法抵挡这种类似于渗透的“优秀”了,经常性的发现自己会偶尔迷失在关于自己优秀的幻想里面了。这个时候我真的好害怕自己会失去自己最初的本质,我想保持自己的平庸,我觉得只有这样我才能走得更远。

后记 今晚又注定是一个不眠夜,最近太多的东西夹杂在一起,感觉压力有点大了。就在今天才进行完的模拟竞选上,这次我坐的位置是前排了;一年下来,我从坐后几排坐到了第一排,这不得不说是我整个大学生涯里面比较大的进步吧。我觉得我很幸运我能拥有很多优秀的同事以及很多优秀的干事,但这次我不会再说他们优秀了。

   这是我第一次开通个人博客,文章写的也很水,对于这个个人博客,起初的想法仅仅只是想把自己刚刚学会的新技术拿来练练手,但是后来又发现在生活中我有太多值得思考的东西了,我需要的是一个能记录我这些琐碎思绪的工具,现在这个博客系统就已经承载了这个责任了。
继续阅读